Android NDK開發(fā)之旅33--NDK-Linux入門之POSIX線程原語

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)境关斜。

  1. 首先需要下載POSIX,地址為:ftp://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip
  2. 創(chuàng)建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下面的動態(tài)庫文件復制到工程的正確位置铺浇。

POSIX的編譯:

  1. VS平臺中直接編譯運行即可痢畜。
  2. 在Linux平臺中采用gcc編譯(先編譯生成目標文件然后鏈接生成可執(zhí)行程序):gcc test.c -o test -lpthread,執(zhí)行:./test。

POSIX幫助文檔的查看:

  1. 在Linux系統(tǒng)中丁稀,安裝POSIX幫助文檔:sudo apt-get install manpages-posix-dev
  2. 列出所有函數(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");
}

在代碼中:

  1. 通過pthread_create創(chuàng)建線程白热,需要傳入一個函數(shù)指針敛助,相當于Java線程中的run方法。然后還需要傳參屋确,參數(shù)可以在run方法中取出纳击。
  2. 線程被創(chuàng)建以后,就會執(zhí)行“run”方法攻臀,該方法中可以拿到線程創(chuàng)建的參數(shù)焕数,可以自殺掉線程。線程的結束需要參數(shù)刨啸。
  3. 可以通過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ù). . .

在代碼中:

  1. 我們通過pthread_mutex_init初始化了一把互斥鎖,最后通過pthread_mutex_destroy進行銷毀骇陈。
  2. 在線程執(zhí)行的時候震庭,我們可以通過pthread_mutex_lock、pthread_mutex_unlock進行加鎖和解鎖你雌。
  3. 使用互斥鎖可以解決線程死鎖(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");
}

如果覺得我的文字對你有所幫助的話垦沉,歡迎關注我的公眾號:

公眾號:Android開發(fā)進階

我的群歡迎大家進來探討各種技術與非技術的話題煌抒,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)厕倍。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寡壮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子讹弯,更是在濱河造成了極大的恐慌况既,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件组民,死亡現(xiàn)場離奇詭異棒仍,居然都是意外死亡,警方通過查閱死者的電腦和手機臭胜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門莫其,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耸三,你說我怎么就攤上這事乱陡。” “怎么了仪壮?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵憨颠,是天一觀的道長。 經(jīng)常有香客問我积锅,道長爽彤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任缚陷,我火速辦了婚禮适篙,結果婚禮上,老公的妹妹穿的比我還像新娘蹬跃。我一直安慰自己匙瘪,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布蝶缀。 她就那樣靜靜地躺著丹喻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翁都。 梳的紋絲不亂的頭發(fā)上碍论,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音柄慰,去河邊找鬼鳍悠。 笑死税娜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的藏研。 我是一名探鬼主播敬矩,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蠢挡!你這毒婦竟也來了弧岳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤业踏,失蹤者是張志新(化名)和其女友劉穎禽炬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勤家,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡腹尖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了伐脖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片热幔。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖讼庇,靈堂內(nèi)的尸體忽然破棺而出断凶,到底是詐尸還是另有隱情,我是刑警寧澤巫俺,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站肿男,受9級特大地震影響介汹,放射性物質發(fā)生泄漏。R本人自食惡果不足惜舶沛,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一嘹承、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧如庭,春花似錦叹卷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至往毡,卻和暖如春蒙揣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背开瞭。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工懒震, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罩息,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓个扰,卻偏偏與公主長得像瓷炮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子递宅,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 引用自多線程編程指南應用程序里面多個線程的存在引發(fā)了多個執(zhí)行線程安全訪問資源的潛在問題娘香。兩個線程同時修改同一資源有...
    Mitchell閱讀 1,984評論 1 7
  • linux線程同步 信號燈:與互斥鎖和條件變量的主要不同在于"燈"的概念,燈亮則意味著資源可用恐锣,燈滅則意味著不可用...
    鮑陳飛閱讀 683評論 0 2
  • 文/新鮮 《創(chuàng)新自信力》 騰躍:從設計思維到創(chuàng)意自信 1.從技術可行性茅主、商業(yè)可行性和人的期望值之間找到共通點,也就...
    新鮮wendy閱讀 818評論 0 0
  • 準備考研的日子土榴,經(jīng)常會很迷茫诀姚,常常想些有的沒的, 回想起小時候的日子玷禽,覺得當時的自己是最純粹的赫段,比起現(xiàn)在而言,...
    二木子閱讀 242評論 0 1