博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android NDK开发之旅33 NDK Linux入门之POSIX线程原语
阅读量:5812 次
发布时间:2019-06-18

本文共 7363 字,大约阅读时间需要 24 分钟。

###POSIX

POSIX是一种标准,例如有多线程编程标准、网络编程标准等。

####POSIX多线程

Linux下,一般多线程的实现由POSIX多线程编程实现。Android系统属于Linux系统,因此NDK原生支持POSIX多线程编程。

Windows平台一般用Windows自带的API。

####Visual Studio平台搭建POSIX多线程环境

因为POSIX多线程是Linux的,因此如果需要在Visual Studio下开发,需要搭建可开发环境。

  1. 首先需要下载POSIX,地址为:
  2. 创建VS空项目
  3. 添加包含目录:pthreads-w32-2-9-1-release\Pre-built.2\include以及pthreads-w32-2-9-1-release\pthreads.2
  4. 添加库目录:pthreads-w32-2-9-1-release\Pre-built.2\lib\x86
  5. 添加附加依赖项:pthreadVC2.lib
  6. 把pthreads-w32-2-9-1-release\Pre-built.2\dll\x86下面的动态库文件复制到工程的正确位置。

POSIX的编译:

  1. VS平台中直接编译运行即可。
  2. 在Linux平台中采用gcc编译(先编译生成目标文件然后链接生成可执行程序):gcc test.c -o test -lpthread,执行:./test。

POSIX帮助文档的查看:

  1. 在Linux系统中,安装POSIX帮助文档:sudo apt-get install manpages-posix-dev
  2. 列出所有函数man -k pthread;查看某个函数:man pthread_create

####创建线程与线程的结束

#include 
#include
//必须引入的头文件#include "pthread.h"//一个相当于Java的run方法void *start_fun(void* arg){ //得到线程创建的参数 char* no = (char*)arg; int i = 0; for (; i < 10; i++){ printf("%s thread : %d \n", no, i); if (i == 5){ //线程自杀,需要返回参数 pthread_exit((void*)2); //线程他杀 //pthread_cancel() } } //run方法执行完,线程结束,返回 return (void*)1;}void main(){ printf("main thread\n"); pthread_t thread; //创建线程,指定run方法,并且可以传入参数,在run方法的arg中可以取出 pthread_create(&thread, NULL, start_fun, "no1"); void* r_val; //等待线程结束,获取线程返回参数 pthread_join(thread, &r_val); printf("return value : %d", (int)r_val); system("pause");}复制代码

在代码中:

  1. 通过pthread_create创建线程,需要传入一个函数指针,相当于Java线程中的run方法。然后还需要传参,参数可以在run方法中取出。
  2. 线程被创建以后,就会执行“run”方法,该方法中可以拿到线程创建的参数,可以自杀掉线程。线程的结束需要参数。
  3. 可以通过pthread_join方法等待线程结束,并且可获取线程结束的参数。

####锁

我们创建两个线程:

#include 
#include
#include "pthread.h"#include
int i = 0;//一个相当于Java的run方法void *start_fun(void* arg){ //得到线程创建的参数 char* no = (char*)arg; for (; i < 10; i++){ Sleep(10); printf("%s thread : %d \n", no, i); } i = 0; //run方法执行完,线程结束,返回 return (void*)1;}void main(){ printf("main thread\n"); pthread_t thread1; pthread_t thread2; //创建线程,指定run方法,并且可以传入参数,在run方法的arg中可以取出 pthread_create(&thread1, NULL, start_fun, "no1"); pthread_create(&thread2, NULL, start_fun, "no2"); void* r_val1; void* r_val2; //等待线程结束,获取线程返回参数 pthread_join(thread1, &r_val1); pthread_join(thread2, &r_val2); printf("return value : %d\n", (int)r_val1); printf("return value : %d\n", (int)r_val2); system("pause");}复制代码

打印的结果如下:

main threadno2 thread : 0no1 thread : 0no2 thread : 2no1 thread : 2no1 thread : 4no2 thread : 4no1 thread : 5return value : 2return value : 2请按任意键继续. . .复制代码

可见,两个线程是并行执行的。i变量同时被两个线程访问。但是我们现在要求线程1先执行完,然后才到线程2执行,那么两个线程i的所有情况都会被打印出来。

这时候我们需要使用互斥锁:

#include 
#include
#include "pthread.h"#include
int i = 0;//互斥锁pthread_mutex_t m;//一个相当于Java的run方法void *start_fun(void* arg){ //加锁 pthread_mutex_lock(&m); //得到线程创建的参数 char* no = (char*)arg; for (; i < 10; i++){ Sleep(10); printf("%s thread : %d \n", no, i); } i = 0; //解锁 pthread_mutex_unlock(&m); //run方法执行完,线程结束,返回 return (void*)1;}void main(){ printf("main thread\n"); //初始化互斥锁 pthread_mutex_init(&m, NULL); pthread_t thread1; pthread_t thread2; //创建线程,指定run方法,并且可以传入参数,在run方法的arg中可以取出 pthread_create(&thread1, NULL, start_fun, "no1"); pthread_create(&thread2, NULL, start_fun, "no2"); void* r_val1; void* r_val2; //等待线程结束,获取线程返回参数 pthread_join(thread1, &r_val1); pthread_join(thread2, &r_val2); printf("return value : %d\n", (int)r_val1); printf("return value : %d\n", (int)r_val2); //销毁互斥锁 pthread_mutex_destroy(&m); system("pause");}复制代码

输出的结果如下:

main threadno1 thread : 0no1 thread : 1no1 thread : 2no1 thread : 3no1 thread : 4no1 thread : 5no1 thread : 6no1 thread : 7no1 thread : 8no1 thread : 9no2 thread : 0no2 thread : 1no2 thread : 2no2 thread : 3no2 thread : 4no2 thread : 5no2 thread : 6no2 thread : 7no2 thread : 8no2 thread : 9return value : 1return value : 1请按任意键继续. . .复制代码

在代码中:

  1. 我们通过pthread_mutex_init初始化了一把互斥锁,最后通过pthread_mutex_destroy进行销毁。
  2. 在线程执行的时候,我们可以通过pthread_mutex_lock、pthread_mutex_unlock进行加锁和解锁。
  3. 使用互斥锁可以解决线程死锁(ABBA)的问题。

互斥锁是先让一个线程做完,然后另外一个线程做。还有一种情况就是,一个线程先执行,生产,然后另外一个线程就会去消费。

其实视频解码的绘制使用的就是生产者--消费者的模式。图片的下载显示也是基于这种模式。比如说我们生产者生成的产品,放到一个队列里面,当生产者生产出产品的时候就会发送信号通知消费者去消费,例如RTMP推流的时候,我们本地采集音视频的时候就需要一种队列,因为本地的压缩比网络上传要快。

使用这一种模式,就需要条件变量。例子:

#include 
#include
#include "pthread.h"#include
//模拟产品队列int productNum = 0;//互斥锁pthread_mutex_t m;//条件变量pthread_cond_t c;void *produce(void* arg){ char* no = (char*)arg; for (;;){ //加锁 pthread_mutex_lock(&m); //生产者生产产品 productNum++; printf("%s生产产品:%d\n", no, productNum); //通知消费者进行消费 pthread_cond_signal(&c); //解锁 pthread_mutex_unlock(&m); Sleep(100); } return (void*)1;}void *comsume(void* arg){ char* no = (char*)arg; for (;;){ pthread_mutex_lock(&m); //使用while是为了防止惊群效应唤醒条件变量 while (productNum == 0){ //1.没有产品可以消费,等待生产者生产,即等待条件变量被唤醒 //2.释放互斥锁,使得其他消费者可以进来等待 //3.被唤醒的时候,解除阻塞,重新申请获得互斥锁,保证只有一个消费者消费 pthread_cond_wait(&c, &m); } productNum--; printf("%s消费者消费产品:%d\n", no, productNum); pthread_mutex_unlock(&m); Sleep(1000); } return (void*)1;}void main(){ printf("main thread\n"); //初始化互斥锁 pthread_mutex_init(&m, NULL); //初始化条件变量 pthread_cond_init(&c, NULL); pthread_t thread_producer; pthread_t thread_comsumer; //创建线程,指定run方法,并且可以传入参数,在run方法的arg中可以取出 pthread_create(&thread_producer, NULL, produce, "producer"); pthread_create(&thread_comsumer, NULL, comsume, "comsumer"); //等待线程结束,获取线程返回参数 pthread_join(thread_producer, NULL); pthread_join(thread_comsumer, NULL); //销毁互斥锁 pthread_mutex_destroy(&m); //销毁条件变量 pthread_cond_destroy(&c); system("pause");}复制代码

输出的结果如下:

main threadproducer生产产品:1comsumer消费者消费产品:0producer生产产品:1producer生产产品:2producer生产产品:3producer生产产品:4producer生产产品:5producer生产产品:6producer生产产品:7producer生产产品:8producer生产产品:9comsumer消费者消费产品:8producer生产产品:9producer生产产品:10producer生产产品:11producer生产产品:12producer生产产品:13producer生产产品:14producer生产产品:15producer生产产品:16producer生产产品:17comsumer消费者消费产品:16复制代码

这里我通过Sleep的方式控制了生产者与消费者的效率,一般来说生产的速度要比消费的速度快。

上面是只有一个生产者和一个消费者的示例代码。一般开说,生产者和消费者都会有多个。这里我们通过线程数组的方式来实现。

示例代码如下:

#include 
#include
#include "pthread.h"#include
#define NUM_PRODUCER 2#define NUM_COMSUMER 2pthread_t threads[NUM_PRODUCER + NUM_COMSUMER];int productNum = 0;//互斥锁pthread_mutex_t m;//条件变量pthread_cond_t c;void *produce(void* arg){ int no = (int)arg; for (;;){ //加锁 pthread_mutex_lock(&m); //生产者生产产品 productNum++; printf("%d生产产品:%d\n", no, productNum); //通知消费者进行消费 pthread_cond_signal(&c); //解锁 pthread_mutex_unlock(&m); Sleep(100); } return (void*)1;}void *comsume(void* arg){ int no = (int)arg; for (;;){ pthread_mutex_lock(&m); while (productNum == 0){ //没有产品可以消费,等待生产者生产 pthread_cond_wait(&c, &m); } productNum--; printf("%d消费者消费产品:%d\n", no, productNum); pthread_mutex_unlock(&m); Sleep(1000); } return (void*)1;}void main(){ printf("main thread\n"); //初始化互斥锁 pthread_mutex_init(&m, NULL); //初始化条件变量 pthread_cond_init(&c, NULL); int i = 0; //创建生产者线程 for (i = 0; i < NUM_PRODUCER; i++){ pthread_create(&threads[i], NULL, produce, (void*)i); } //创建消费者线程 for (i = 0; i < NUM_COMSUMER; i++){ pthread_create(&threads[NUM_PRODUCER + i], NULL, comsume, (void*)i); } //等待线程结束,获取线程返回参数 for (i = 0; i < NUM_PRODUCER + NUM_COMSUMER; i++){ pthread_join(threads[i], NULL); } //销毁互斥锁 pthread_mutex_destroy(&m); //销毁条件变量 pthread_cond_destroy(&c); system("pause");}复制代码

如果觉得我的文字对你有所帮助的话,欢迎关注我的公众号:

我的群欢迎大家进来探讨各种技术与非技术的话题,有兴趣的朋友们加我私人微信huannan88,我拉你进群交(♂)流(♀)

转载地址:http://ditbx.baihongyu.com/

你可能感兴趣的文章
微信公众号与APP微信第三方登录账号打通
查看>>
onchange()事件的应用
查看>>
PowerPoint 2010 设置演讲者模式
查看>>
net 和Mono 构建的HTTP服务框架
查看>>
Windows 下最佳的 C++ 开发的 IDE 是什么?
查看>>
软件工程师成长为架构师必备的十项技能
查看>>
python 异常
查看>>
百度账号注销
查看>>
jquery画图插件jPainter
查看>>
真正努力的人,从来不焦虑
查看>>
Lua语言特色
查看>>
vscode忽略node_module
查看>>
C#泛型-什么是泛型
查看>>
regsvr32.exe进程注册dll文件
查看>>
C# 单机Window 程序 sqlite 数据库实现
查看>>
JavaScript一些函数
查看>>
国务院关于积极推进“互联网+”行动的指导意见
查看>>
Matrix Factorization, Algorithms, Applications, and Avaliable packages
查看>>
图像配准转换
查看>>
mysql-This version of MySQL doesn’t yet support ‘LIMIT & IN/ALL/ANY/SOME 错误解决
查看>>