POSIX
POSIX是一種標準宿百,例如有多線程編程標準洲拇、網(wǎng)絡編程標準等。
POSIX多線程
Linux下绅项,一般多線程的實現(xiàn)由POSIX多線程編程實現(xiàn)紊册。Android系統(tǒng)屬于Linux系統(tǒng),因此NDK原生支持POSIX多線程編程快耿。
Windows平臺一般用Windows自帶的API囊陡。
Visual Studio平臺搭建POSIX多線程環(huán)境
因為POSIX多線程是Linux的,因此如果需要在Visual Studio下開發(fā)润努,需要搭建可開發(fā)環(huán)境关斜。
- 首先需要下載POSIX,地址為:ftp://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip
- 創(chuàng)建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下面的動態(tài)庫文件復制到工程的正確位置铺浇。
POSIX的編譯:
- VS平臺中直接編譯運行即可痢畜。
- 在Linux平臺中采用gcc編譯(先編譯生成目標文件然后鏈接生成可執(zhí)行程序):gcc test.c -o test -lpthread,執(zhí)行:./test。
POSIX幫助文檔的查看:
- 在Linux系統(tǒng)中丁稀,安裝POSIX幫助文檔:sudo apt-get install manpages-posix-dev
- 列出所有函數(shù)man -k pthread吼拥;查看某個函數(shù):man pthread_create
創(chuàng)建線程與線程的結束
#include <stdlib.h>
#include <stdio.h>
//必須引入的頭文件
#include "pthread.h"
//一個相當于Java的run方法
void *start_fun(void* arg){
//得到線程創(chuàng)建的參數(shù)
char* no = (char*)arg;
int i = 0;
for (; i < 10; i++){
printf("%s thread : %d \n", no, i);
if (i == 5){
//線程自殺,需要返回參數(shù)
pthread_exit((void*)2);
//線程他殺
//pthread_cancel()
}
}
//run方法執(zhí)行完线衫,線程結束凿可,返回
return (void*)1;
}
void main(){
printf("main thread\n");
pthread_t thread;
//創(chuàng)建線程,指定run方法授账,并且可以傳入?yún)?shù)枯跑,在run方法的arg中可以取出
pthread_create(&thread, NULL, start_fun, "no1");
void* r_val;
//等待線程結束,獲取線程返回參數(shù)
pthread_join(thread, &r_val);
printf("return value : %d", (int)r_val);
system("pause");
}
在代碼中:
- 通過pthread_create創(chuàng)建線程白热,需要傳入一個函數(shù)指針敛助,相當于Java線程中的run方法。然后還需要傳參屋确,參數(shù)可以在run方法中取出纳击。
- 線程被創(chuàng)建以后,就會執(zhí)行“run”方法攻臀,該方法中可以拿到線程創(chuàng)建的參數(shù)焕数,可以自殺掉線程。線程的結束需要參數(shù)刨啸。
- 可以通過pthread_join方法等待線程結束堡赔,并且可獲取線程結束的參數(shù)。
鎖
我們創(chuàng)建兩個線程:
#include <stdlib.h>
#include <stdio.h>
#include "pthread.h"
#include <Windows.h>
int i = 0;
//一個相當于Java的run方法
void *start_fun(void* arg){
//得到線程創(chuàng)建的參數(shù)
char* no = (char*)arg;
for (; i < 10; i++){
Sleep(10);
printf("%s thread : %d \n", no, i);
}
i = 0;
//run方法執(zhí)行完呜投,線程結束加匈,返回
return (void*)1;
}
void main(){
printf("main thread\n");
pthread_t thread1;
pthread_t thread2;
//創(chuàng)建線程,指定run方法仑荐,并且可以傳入?yún)?shù)雕拼,在run方法的arg中可以取出
pthread_create(&thread1, NULL, start_fun, "no1");
pthread_create(&thread2, NULL, start_fun, "no2");
void* r_val1;
void* r_val2;
//等待線程結束,獲取線程返回參數(shù)
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 thread
no2 thread : 0
no1 thread : 0
no2 thread : 2
no1 thread : 2
no1 thread : 4
no2 thread : 4
no1 thread : 5
return value : 2
return value : 2
請按任意鍵繼續(xù). . .
可見粘招,兩個線程是并行執(zhí)行的啥寇。i變量同時被兩個線程訪問。但是我們現(xiàn)在要求線程1先執(zhí)行完洒扎,然后才到線程2執(zhí)行辑甜,那么兩個線程i的所有情況都會被打印出來。
這時候我們需要使用互斥鎖:
#include <stdlib.h>
#include <stdio.h>
#include "pthread.h"
#include <Windows.h>
int i = 0;
//互斥鎖
pthread_mutex_t m;
//一個相當于Java的run方法
void *start_fun(void* arg){
//加鎖
pthread_mutex_lock(&m);
//得到線程創(chuàng)建的參數(shù)
char* no = (char*)arg;
for (; i < 10; i++){
Sleep(10);
printf("%s thread : %d \n", no, i);
}
i = 0;
//解鎖
pthread_mutex_unlock(&m);
//run方法執(zhí)行完袍冷,線程結束磷醋,返回
return (void*)1;
}
void main(){
printf("main thread\n");
//初始化互斥鎖
pthread_mutex_init(&m, NULL);
pthread_t thread1;
pthread_t thread2;
//創(chuàng)建線程,指定run方法胡诗,并且可以傳入?yún)?shù)邓线,在run方法的arg中可以取出
pthread_create(&thread1, NULL, start_fun, "no1");
pthread_create(&thread2, NULL, start_fun, "no2");
void* r_val1;
void* r_val2;
//等待線程結束淌友,獲取線程返回參數(shù)
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 thread
no1 thread : 0
no1 thread : 1
no1 thread : 2
no1 thread : 3
no1 thread : 4
no1 thread : 5
no1 thread : 6
no1 thread : 7
no1 thread : 8
no1 thread : 9
no2 thread : 0
no2 thread : 1
no2 thread : 2
no2 thread : 3
no2 thread : 4
no2 thread : 5
no2 thread : 6
no2 thread : 7
no2 thread : 8
no2 thread : 9
return value : 1
return value : 1
請按任意鍵繼續(xù). . .
在代碼中:
- 我們通過pthread_mutex_init初始化了一把互斥鎖,最后通過pthread_mutex_destroy進行銷毀骇陈。
- 在線程執(zhí)行的時候震庭,我們可以通過pthread_mutex_lock、pthread_mutex_unlock進行加鎖和解鎖你雌。
- 使用互斥鎖可以解決線程死鎖(ABBA)的問題器联。
互斥鎖是先讓一個線程做完,然后另外一個線程做婿崭。還有一種情況就是拨拓,一個線程先執(zhí)行,生產(chǎn)氓栈,然后另外一個線程就會去消費千元。
其實視頻解碼的繪制使用的就是生產(chǎn)者--消費者的模式。圖片的下載顯示也是基于這種模式颤绕。比如說我們生產(chǎn)者生成的產(chǎn)品,放到一個隊列里面祟身,當生產(chǎn)者生產(chǎn)出產(chǎn)品的時候就會發(fā)送信號通知消費者去消費奥务,例如RTMP推流的時候,我們本地采集音視頻的時候就需要一種隊列袜硫,因為本地的壓縮比網(wǎng)絡上傳要快氯葬。
使用這一種模式,就需要條件變量婉陷。例子:
#include <stdlib.h>
#include <stdio.h>
#include "pthread.h"
#include <Windows.h>
//模擬產(chǎn)品隊列
int productNum = 0;
//互斥鎖
pthread_mutex_t m;
//條件變量
pthread_cond_t c;
void *produce(void* arg){
char* no = (char*)arg;
for (;;){
//加鎖
pthread_mutex_lock(&m);
//生產(chǎn)者生產(chǎn)產(chǎn)品
productNum++;
printf("%s生產(chǎn)產(chǎn)品:%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.沒有產(chǎn)品可以消費帚称,等待生產(chǎn)者生產(chǎn),即等待條件變量被喚醒
//2.釋放互斥鎖秽澳,使得其他消費者可以進來等待
//3.被喚醒的時候闯睹,解除阻塞,重新申請獲得互斥鎖担神,保證只有一個消費者消費
pthread_cond_wait(&c, &m);
}
productNum--;
printf("%s消費者消費產(chǎn)品:%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;
//創(chuàng)建線程楼吃,指定run方法,并且可以傳入?yún)?shù)妄讯,在run方法的arg中可以取出
pthread_create(&thread_producer, NULL, produce, "producer");
pthread_create(&thread_comsumer, NULL, comsume, "comsumer");
//等待線程結束孩锡,獲取線程返回參數(shù)
pthread_join(thread_producer, NULL);
pthread_join(thread_comsumer, NULL);
//銷毀互斥鎖
pthread_mutex_destroy(&m);
//銷毀條件變量
pthread_cond_destroy(&c);
system("pause");
}
輸出的結果如下:
main thread
producer生產(chǎn)產(chǎn)品:1
comsumer消費者消費產(chǎn)品:0
producer生產(chǎn)產(chǎn)品:1
producer生產(chǎn)產(chǎn)品:2
producer生產(chǎn)產(chǎn)品:3
producer生產(chǎn)產(chǎn)品:4
producer生產(chǎn)產(chǎn)品:5
producer生產(chǎn)產(chǎn)品:6
producer生產(chǎn)產(chǎn)品:7
producer生產(chǎn)產(chǎn)品:8
producer生產(chǎn)產(chǎn)品:9
comsumer消費者消費產(chǎn)品:8
producer生產(chǎn)產(chǎn)品:9
producer生產(chǎn)產(chǎn)品:10
producer生產(chǎn)產(chǎn)品:11
producer生產(chǎn)產(chǎn)品:12
producer生產(chǎn)產(chǎn)品:13
producer生產(chǎn)產(chǎn)品:14
producer生產(chǎn)產(chǎn)品:15
producer生產(chǎn)產(chǎn)品:16
producer生產(chǎn)產(chǎn)品:17
comsumer消費者消費產(chǎn)品:16
這里我通過Sleep的方式控制了生產(chǎn)者與消費者的效率,一般來說生產(chǎn)的速度要比消費的速度快亥贸。
上面是只有一個生產(chǎn)者和一個消費者的示例代碼躬窜。一般開說,生產(chǎn)者和消費者都會有多個炕置。這里我們通過線程數(shù)組的方式來實現(xiàn)荣挨。
示例代碼如下:
#include <stdlib.h>
#include <stdio.h>
#include "pthread.h"
#include <Windows.h>
#define NUM_PRODUCER 2
#define NUM_COMSUMER 2
pthread_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);
//生產(chǎn)者生產(chǎn)產(chǎn)品
productNum++;
printf("%d生產(chǎn)產(chǎn)品:%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){
//沒有產(chǎn)品可以消費男韧,等待生產(chǎn)者生產(chǎn)
pthread_cond_wait(&c, &m);
}
productNum--;
printf("%d消費者消費產(chǎn)品:%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;
//創(chuàng)建生產(chǎn)者線程
for (i = 0; i < NUM_PRODUCER; i++){
pthread_create(&threads[i], NULL, produce, (void*)i);
}
//創(chuàng)建消費者線程
for (i = 0; i < NUM_COMSUMER; i++){
pthread_create(&threads[NUM_PRODUCER + i], NULL, comsume, (void*)i);
}
//等待線程結束,獲取線程返回參數(shù)
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,我拉你進群交(♂)流(♀)厕倍。