###POSIX
POSIX是一种标准,例如有多线程编程标准、网络编程标准等。
####POSIX多线程
Linux下,一般多线程的实现由POSIX多线程编程实现。Android系统属于Linux系统,因此NDK原生支持POSIX多线程编程。
Windows平台一般用Windows自带的API。
####Visual Studio平台搭建POSIX多线程环境
因为POSIX多线程是Linux的,因此如果需要在Visual Studio下开发,需要搭建可开发环境。
- 首先需要下载POSIX,地址为:
- 创建VS空项目
- 添加包含目录:pthreads-w32-2-9-1-release\Pre-built.2\include以及pthreads-w32-2-9-1-release\pthreads.2
- 添加库目录:pthreads-w32-2-9-1-release\Pre-built.2\lib\x86
- 添加附加依赖项:pthreadVC2.lib
- 把pthreads-w32-2-9-1-release\Pre-built.2\dll\x86下面的动态库文件复制到工程的正确位置。
POSIX的编译:
- VS平台中直接编译运行即可。
- 在Linux平台中采用gcc编译(先编译生成目标文件然后链接生成可执行程序):gcc test.c -o test -lpthread,执行:./test。
POSIX帮助文档的查看:
- 在Linux系统中,安装POSIX帮助文档:sudo apt-get install manpages-posix-dev
- 列出所有函数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");}复制代码
在代码中:
- 通过pthread_create创建线程,需要传入一个函数指针,相当于Java线程中的run方法。然后还需要传参,参数可以在run方法中取出。
- 线程被创建以后,就会执行“run”方法,该方法中可以拿到线程创建的参数,可以自杀掉线程。线程的结束需要参数。
- 可以通过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请按任意键继续. . .复制代码
在代码中:
- 我们通过pthread_mutex_init初始化了一把互斥锁,最后通过pthread_mutex_destroy进行销毁。
- 在线程执行的时候,我们可以通过pthread_mutex_lock、pthread_mutex_unlock进行加锁和解锁。
- 使用互斥锁可以解决线程死锁(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,我拉你进群交(♂)流(♀)。