Linux入门之POSIX线程原语

1. POSIX简介

POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为POSIX),POSIX标准定义了操作系统为应用程序提供的接口标准。
这里介绍的是关于POSIX线程的内容,POSIX线程的API都在pthread.h文件中,如果已经安装了,可以通过以下命令查看所有API:

man -k pthread

查看某个API的用法:

man pthread_create

如果没有安装,可以通过以下命令去安装POSIX文档:

sudo apt-get install manpages-posix-dev

2. 创建线程与线程结束:

新建01.c文件:

  1 #include<stdlib.h>
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<pthread.h>
  5 
  6 void* thr_fun(void* arg) {
  7     char* no = (char*)arg;
  8     int i = 0;
  9     for(; i < 10; i++) {
 10         printf("thread:%s,i:%d\n", no, i);
 11         if(i == 5) {
 12             //线程结束(自杀)                                                                                                                                         
 13             pthread_exit((void*)2);
 14         }
 15     }
 16     return (void*)1;
 17 }
 18 
 19 void main() {
 20     printf("man thread\n");
 21     //线程的id
 22     pthread_t tid;
 23     //第二个参数表示属性,NULL表示默认属性
 24     //thr_fun,线程创建之后执行的函数
 25     pthread_create(&tid, NULL, thr_fun, "1");
 26     void* retval;
 27     //等待tid线程结束
 28     //thr_fun与pthread_exit退出时的参数,都作为第二个参数的内容
 29     pthread_join(tid, &retval);
 30     printf("retval:%d\n", (int)retval);
 31 }

上述线程自杀用pthread_exit(),而线程他杀用pthread_cancel()
将01.c文件编译成可执行文件:

gcc 01.c -o 01 -lpthread

这时候在该目录下生成01文件,此时我们就可以执行该文件了:

./01

输出为:

man thread
thread:1,i:0
thread:1,i:1
thread:1,i:2
thread:1,i:3
thread:1,i:4
thread:1,i:5
retval:2

3. 互斥锁的使用

新建02.c文件:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<pthread.h>
  5 
  6 int i = 0;
  7 //互斥锁
  8 pthread_mutex_t mutex;
  9 void* thr_fun(void* arg) {
 10     //加锁
 11     pthread_mutex_lock(&mutex);
 12     char* no = (char*)arg;
 13     for(; i < 5; i++) {
 14         printf("thread:%s,i:%d\n", no, i);
 15         sleep(1);
 16     }
 17     i = 0;
 18     //解锁
 19     pthread_mutex_unlock(&mutex);
 20 }
 21 
 22 void main() {
 23     pthread_t tid1, tid2;
 24     //初始化互斥锁
 25     pthread_mutex_init(&mutex, NULL);
 26 
 27     pthread_create(&tid1, NULL, thr_fun, "NO1");
 28     pthread_create(&tid2, NULL, thr_fun, "NO2");
 29 
 30     pthread_join(tid1, NULL);
 31     pthread_join(tid2, NULL);
 32     
 33     //销毁互斥锁
 34     pthread_mutex_destroy(&mutex);
 35 }  

上面的代码中:

  1. 我们通过pthread_mutex_init先初始化互斥锁,再通过创建NO1线程和NO2线程,通过pthread_join去阻塞主线程,直到子线程结束,最后销毁互斥锁
  2. 在某个线程执行的时候,首先通过pthread_mutex_lock加锁,当该线程执行完了之后,再通过pthread_mutex_unlock释放锁,让另外的一个线程去加锁。
  3. 互斥锁就是将一个线程做完,然后另一个线程再做。

编译02.c文件:

gcc 02.c -o 02 -lpthread

运行02文件:

thread:NO2,i:0
thread:NO2,i:1
thread:NO2,i:2
thread:NO2,i:3
thread:NO2,i:4
thread:NO1,i:0
thread:NO1,i:1
thread:NO1,i:2
thread:NO1,i:3
thread:NO1,i:4

可以发现,先将线程NO2执行完了,才会执行线程NO1,这就是互斥锁的作用所在。

我在shell5中查找pthread_create是可以查到用法的,但是我查找pthread_mutex_init中是无法查找的,也试过先更新sudo,再使用下面的命令下载也不行:

sudo apt-get install manpages-posix-dev

后来发现这是man不完整的原因,利用下列命令去下载完整版:

sudo apt-get update

安装标准C的帮助文档

sudo apt-get install libc-dev
sudo apt-get install glibc-doc

下载过程中遇到y/n,一律输入y进行下载。最后查看pthread_mutex_init就可以了

man pthread_mutex_init

4. 生产者、消费者模型

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

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<pthread.h>
  5 
  6 //互斥锁
  7 pthread_mutex_t mutex;
  8 //条件变量
  9 pthread_cond_t has_product;
 10 //队列
 11 int ready;
 12 
 13 //生产着
 14 void* thr_producer(void* arg) {
 15     int no = (int)arg;
 16     for(;;) {
 17         //加锁
 18         pthread_mutex_lock(&mutex);
 19         //往队列中添加产品
 20         ready++;
 21         printf("producer %d produce\n", no);
 22         //通知消费者,有新的产品可以消费了
 23         pthread_cond_signal(&has_product);
 24         //解锁
 25         pthread_mutex_unlock(&mutex);
 26         sleep(1);
 27       }
 28    }
 29
 30 void* thr_consumer(void* arg) {
 33         pthread_mutex_lock(&mutex);
 34         if(ready == 0) {
 35             //没有产品,继续等待
 36             pthread_cond_wait(&has_product, &mutex);
 37             printf("consumer %d wait\n", no);
 38         }
 39         //有产品,消费产品
 40         ready--;
 41         printf("consumer %d consume\n", no);
 42         sleep(1);
 43         pthread_mutex_unlock(&mutex);
 44     }
 45 }
 46 
 47 void main() {
 48     //初始化互斥锁和条件变量
 49     pthread_mutex_init(&mutex, NULL);
 50     pthread_cond_init(&has_product, NULL);
 51 
 52     pthread_t tid_p, tid_c;
 53     //生产者线程
 54     pthread_create(&tid_p, NULL, thr_producer, 1);
 55     //消费者线程
 56     pthread_create(&tid_c, NULL, thr_consumer, 2);
 57     
 58     //等待
 59     pthread_join(tid_p, NULL);
 60     pthread_join(tid_c, NULL);
 61 
 62     //销毁
 63     pthread_mutex_destroy(&mutex);
 64     pthread_cond_destroy(&has_product);
 65 }

上面的代码中我们创建了两个线程,分别是生产者线程和消费者线程,生产者线程循环添加产品,添加完了之后通过消费者去消费,消费者发现有产品就消费产品,没有产品,就继续等待,两个线程都是利用互斥锁保证本身线程的执行。

但是这样单独一个生产者线程和消费者线程显然是不够的,实际使用中往往是多个生产者线程和消费者线程,接下来我将循环产生多个生产者线程和消费者线程:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<pthread.h>
  5 #define PRODUCER_NUM 1
  6 #define CONSUMER_NUM 2
  7 pthread_t pids[PRODUCER_NUM + CONSUMER_NUM];
  8 //互斥锁
  9 pthread_mutex_t mutex;
 10 //条件变量
 11 pthread_cond_t has_product;
 12 
 13 //产品队列
 14 int ready = 0;
 15 
 16 //生产者
 17 void* thr_producer(void* arg) {
 18     int no = (int)arg;
 19     for(;;) {
 20         //加锁
 21         pthread_mutex_lock(&mutex);
 22         //往队列中添加产品
 23         ready++;
 24         printf("producer %d produce\n", no);
 25         //通知消费者,有新的产品可以消费了
 26         pthread_cond_signal(&has_product);
 27         //解锁
 28         pthread_mutex_unlock(&mutex);
 29         sleep(1);
 30     }
 31 }
 32 
 33 //消费者
 34 void* thr_consumer(void* arg) {
 35     int no = (int)arg;
 36     for(;;) {
 37         //加锁
 38         pthread_mutex_lock(&mutex);
 39         while(ready == 0) {
 40             printf("consumer %d wait\n", no);
 41             //没有产品,继续等待
 42             //1.阻塞 等待has_product被唤醒
 43             //2.释放互斥锁,pthread_mutex_unlock
 44             //3.被唤醒时,解除阻塞,重新申请获得互斥锁 pthread_mutex_lock
 45             pthread_cond_wait(&has_product, &mutex);
 46         }
 47         //有产品,消费产品
 48         ready--;
 49         printf("consumer %d consume\n", no);
 50         //解锁
 51         pthread_mutex_unlock(&mutex);
 52         sleep(3);
 53     }
 54 }
 55 
 56 void main() {
 57     //初始化互斥锁和条件变量
 58     pthread_mutex_init(&mutex, NULL);
 59     pthread_cond_init(&has_product, NULL);
 60     int i = 0;
 61     for(; i < PRODUCER_NUM; i++) {
 62         //生产者线程
 63         pthread_create(&pids[i], NULL, thr_producer, (void*)i);
 64     }
 65     
 66     i = 0;
 67     for(; i < CONSUMER_NUM; i++) {
 68         //消费者线程
 69         pthread_create(&pids[PRODUCER_NUM + i], NULL, thr_consumer, (void*)i);
 70     }
 71 
 72     i = 0;
 73     for(; i < PRODUCER_NUM + CONSUMER_NUM; i++) {
 74         //等待
 75         pthread_join(pids[i], NULL);
 76     }
 77     
 78     //销毁互斥锁和条件变量
 79     pthread_mutex_destroy(&mutex);
 80     pthread_cond_destroy(&has_product);
 81 }

编译生成可执行文件:

gcc 03.c -o 03 -lpthread

执行文件:

./03

执行后终端截图如下:

1.png

可以按Ctrl + c停止。

喜欢本篇博客的简友们,就请来一波点赞,您的每一次关注,将成为我前进的动力,谢谢!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容