1. POSIX簡介
POSIX表示可移植操作系統(tǒng)接口(Portable Operating System Interface of UNIX鸽斟,縮寫為POSIX),POSIX標(biāo)準(zhǔn)定義了操作系統(tǒng)為應(yīng)用程序提供的接口標(biāo)準(zhǔn)利诺。
這里介紹的是關(guān)于POSIX線程的內(nèi)容富蓄,POSIX線程的API都在pthread.h文件中,如果已經(jīng)安裝了慢逾,可以通過以下命令查看所有API:
man -k pthread
查看某個API的用法:
man pthread_create
如果沒有安裝立倍,可以通過以下命令去安裝POSIX文檔:
sudo apt-get install manpages-posix-dev
2. 創(chuàng)建線程與線程結(jié)束:
新建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 //線程結(jié)束(自殺)
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 //第二個參數(shù)表示屬性灭红,NULL表示默認(rèn)屬性
24 //thr_fun,線程創(chuàng)建之后執(zhí)行的函數(shù)
25 pthread_create(&tid, NULL, thr_fun, "1");
26 void* retval;
27 //等待tid線程結(jié)束
28 //thr_fun與pthread_exit退出時的參數(shù)口注,都作為第二個參數(shù)的內(nèi)容
29 pthread_join(tid, &retval);
30 printf("retval:%d\n", (int)retval);
31 }
上述線程自殺用pthread_exit()
变擒,而線程他殺用pthread_cancel()
將01.c文件編譯成可執(zhí)行文件:
gcc 01.c -o 01 -lpthread
這時候在該目錄下生成01文件,此時我們就可以執(zhí)行該文件了:
./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 }
上面的代碼中:
- 我們通過pthread_mutex_init先初始化互斥鎖娇斑,再通過創(chuàng)建NO1線程和NO2線程,通過pthread_join去阻塞主線程材部,直到子線程結(jié)束毫缆,最后銷毀互斥鎖
- 在某個線程執(zhí)行的時候,首先通過pthread_mutex_lock加鎖乐导,當(dāng)該線程執(zhí)行完了之后苦丁,再通過pthread_mutex_unlock釋放鎖,讓另外的一個線程去加鎖物臂。
- 互斥鎖就是將一個線程做完旺拉,然后另一個線程再做。
編譯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
可以發(fā)現(xiàn)冰抢,先將線程NO2執(zhí)行完了,才會執(zhí)行線程NO1艘狭,這就是互斥鎖的作用所在挎扰。
我在shell5中查找pthread_create是可以查到用法的,但是我查找pthread_mutex_init中是無法查找的巢音,也試過先更新sudo遵倦,再使用下面的命令下載也不行:
sudo apt-get install manpages-posix-dev
后來發(fā)現(xiàn)這是man不完整的原因,利用下列命令去下載完整版:
sudo apt-get update
安裝標(biāo)準(zhǔn)C的幫助文檔
sudo apt-get install libc-dev
sudo apt-get install glibc-doc
下載過程中遇到y(tǒng)/n官撼,一律輸入y進行下載梧躺。最后查看pthread_mutex_init就可以了
man pthread_mutex_init
4. 生產(chǎn)者、消費者模型
上面我們已經(jīng)了解了互斥鎖的用法了傲绣,接下來我們用互斥鎖和條件變量來實現(xiàn)生產(chǎn)者掠哥、消費者模型巩踏。
其實視頻解碼的繪制使用就是生產(chǎn)者--消費者模型。圖片的下載顯示也是基于這種模型续搀。比如說我們生產(chǎn)者生成的產(chǎn)品塞琼,放到一個隊列里面,當(dāng)生產(chǎn)者生產(chǎn)出產(chǎn)品的時候就會發(fā)送信號通知消費者去消費目代,例如RTMP推流的時候屈梁,我們本地采集音視頻的時候就需要一種隊列捣染,因為本地的壓縮比網(wǎng)絡(luò)上傳要快姊氓。
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 //生產(chǎn)著
14 void* thr_producer(void* arg) {
15 int no = (int)arg;
16 for(;;) {
17 //加鎖
18 pthread_mutex_lock(&mutex);
19 //往隊列中添加產(chǎn)品
20 ready++;
21 printf("producer %d produce\n", no);
22 //通知消費者,有新的產(chǎn)品可以消費了
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 //沒有產(chǎn)品狼钮,繼續(xù)等待
36 pthread_cond_wait(&has_product, &mutex);
37 printf("consumer %d wait\n", no);
38 }
39 //有產(chǎn)品霜大,消費產(chǎn)品
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 //生產(chǎn)者線程
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 }
上面的代碼中我們創(chuàng)建了兩個線程构哺,分別是生產(chǎn)者線程和消費者線程,生產(chǎn)者線程循環(huán)添加產(chǎn)品战坤,添加完了之后通過消費者去消費曙强,消費者發(fā)現(xiàn)有產(chǎn)品就消費產(chǎn)品,沒有產(chǎn)品途茫,就繼續(xù)等待碟嘴,兩個線程都是利用互斥鎖保證本身線程的執(zhí)行。
但是這樣單獨一個生產(chǎn)者線程和消費者線程顯然是不夠的囊卜,實際使用中往往是多個生產(chǎn)者線程和消費者線程娜扇,接下來我將循環(huán)產(chǎn)生多個生產(chǎn)者線程和消費者線程:
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 //產(chǎn)品隊列
14 int ready = 0;
15
16 //生產(chǎn)者
17 void* thr_producer(void* arg) {
18 int no = (int)arg;
19 for(;;) {
20 //加鎖
21 pthread_mutex_lock(&mutex);
22 //往隊列中添加產(chǎn)品
23 ready++;
24 printf("producer %d produce\n", no);
25 //通知消費者,有新的產(chǎn)品可以消費了
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 //沒有產(chǎn)品栅组,繼續(xù)等待
42 //1.阻塞 等待has_product被喚醒
43 //2.釋放互斥鎖雀瓢,pthread_mutex_unlock
44 //3.被喚醒時,解除阻塞玉掸,重新申請獲得互斥鎖 pthread_mutex_lock
45 pthread_cond_wait(&has_product, &mutex);
46 }
47 //有產(chǎn)品刃麸,消費產(chǎn)品
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 //生產(chǎn)者線程
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 }
編譯生成可執(zhí)行文件:
gcc 03.c -o 03 -lpthread
執(zhí)行文件:
./03
執(zhí)行后終端截圖如下:
可以按Ctrl + c停止。
喜歡本篇博客的簡友們司浪,就請來一波點贊泊业,您的每一次關(guān)注,將成為我前進的動力啊易,謝謝脱吱!