1. 程式人生 > >Linux多執行緒程式設計

Linux多執行緒程式設計

執行緒概念

    執行緒是指執行中的程式的排程單位。一個執行緒指的是程序中一個單一順序的控制流,也被稱為輕量級執行緒。它是系統獨立排程和分配的基本單位。同一程序中的多個執行緒將共享該系統中的全部系統資源,比如檔案描述符和訊號處理等。一個程序可以有很多執行緒,每個執行緒並行執行不同的任務。

執行緒與程序比較

   ①  和程序相比,它是一種非常“節儉”的多工操作方式。在Linux系統中,啟動一個新的程序必須分配給它獨立的地址空間,建立眾多的資料表來維護其程式碼段、堆疊段和資料段,這種多工工作方式的代價非常“昂貴”。而運行於一個程序中的多個執行緒,它們彼此之間使用相同的地址空間,共享大部分資料,啟動一個執行緒所花費的空間遠遠小於啟動一個程序所花費的空間,而且執行緒間彼此切換所需要時間也遠遠小於程序間切換所需要的時間。

   ②  執行緒間方便的通訊機制。對不同程序來說它們具有獨立的資料空間,要進行資料的傳遞只能通過通訊的方式進行。這種方式不僅費時,而且很不方便。執行緒則不然,由於同一程序下的執行緒之間共享資料空間,所以一個執行緒的資料可以直接為其他執行緒所用,不僅方便,而且快捷。

執行緒基本程式設計

   Linux系統下的多執行緒遵循POSIX執行緒介面,稱為pthread。編寫Linux下的多執行緒程式,需要使用標頭檔案pthread.h,連線時需要使用庫libpthread.a。因為pthread的庫不是Linux系統的庫,所以在編譯時要加上 -lpthread。例如:gcc  filename  -lpthread。注意,這裡要講的執行緒相關操作都是使用者空間中的執行緒的操作。

   執行緒建立:建立執行緒實際上就是確定呼叫該執行緒函式的入口點,這裡通常使用的函式是pthread_create()。線上程建立後,就開始執行相關的執行緒函式。

  執行緒退出:線上程建立後,就開始執行相關的執行緒函式,在該函式執行完之後,該執行緒也就退出了,這也是執行緒退出的一種方法。另一種退出執行緒的方法是使用函式pthread_exit(),這是執行緒的主動行為。這裡要注意的是,在使用執行緒函式時,不能隨意使用exit()退出函式來進行出錯處理。由於exit()的作用是使呼叫程序終止,而一個程序往往包含多個執行緒,因此,在使用exit()之後,該程序中的所有執行緒都終止了。線上程中就可以使用pthread_exit()來代替程序中的exit()。

  執行緒等待:由於一個程序中的多個執行緒是共享資料段的,因此,通常線上程退出後,退出執行緒所佔用的資源並不會隨著執行緒的終止而得到釋放。正如程序之間可以用wait()系統呼叫來同步終止並釋放資源一樣,執行緒之間也有類似機制,那就是pthread_join()函式。pthread_join()用於將當前程序掛起來等待執行緒的結束。這個函式是一個執行緒阻塞的函式,呼叫它的函式將一直等待到被等待的執行緒結束為止,當函式返回時,被等待執行緒的資源就被收回。

  執行緒取消:前面已經提到執行緒呼叫pthread_exit()函式主動終止自身執行緒,但是在很多執行緒應用中,經常會遇到在別的執行緒中要終止另一個執行緒的問題,此時呼叫pthread_cancel()函式來實現這種功能,但在被取消的執行緒的內部需要呼叫pthread_setcancel()函式和pthread_setcanceltype()函式設定自己的取消狀態。例如,被取消的執行緒接收到另一個執行緒的取消請求之後,是接受函式忽略這個請求;如果是接受,則再判斷立刻採取終止操作還是等待某個函式的呼叫等。

  執行緒識別符號獲取:獲取呼叫執行緒的標識ID。

  執行緒清除:執行緒終止有兩種情況:正常終止和非正常終止。執行緒主動呼叫pthread_exit()或者從執行緒函式中return都將使執行緒正常退出,這是可預見的退出方式;非正常終止是執行緒在其它執行緒的干預下,或者由於自身執行出錯(比如訪問非法地址)而退出,這種退出方式是不可預見的。不論是可預見的執行緒終止還是異常終止,都回存在資源釋放的問題,如何保證執行緒終止時能順利地釋放掉自己所佔用的資源,是一個必須考慮的問題。

  從pthread_cleanup_push()的呼叫點到pthread_cleanup_pop()之間的程式段中的終止動作(包括呼叫pthread_exit()和異常終止,不包括return)都將執行pthread_cleanup_push()所指定的清理函式。

1、如何利用2個條件變數實現執行緒同步?

  思路:就是來回的利用pthread_cond_signal()函式,當一方被阻塞時,喚醒函式可以喚醒pthread_cond_wait()函式,只不過pthread_cond_wait()這個方法要執行其後的語句,必須遇到下一個阻塞(也就是pthread_cond_wait()方法時),才執行喚醒後的其後語句。

程式碼如下:

#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<pthread.h> #define MAX_NUM 2static int count = 1;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t js = PTHREAD_COND_INITIALIZER;pthread_cond_t os = PTHREAD_COND_INITIALIZER;void* A(void *arg){    pthread_mutex_lock(&mutex);    while(count <= MAX_NUM)    {        if(count%2 == 1){            printf("A = %d\n", count);            count++;            pthread_cond_signal(&os);            sleep(5);            printf("bbbbbbbbbbbbbbbbbbbbbbbbbbb\n");        }else{            printf("ccccccccccccccccccccccccccc\n");            pthread_cond_wait(&js, &mutex);            printf("ddddddddddddddddddddddddddd\n");        }        pthread_mutex_unlock(&mutex);    }}    void* B(void *arg)    {        pthread_mutex_lock(&mutex);        while(count <= MAX_NUM){            if(count%2 == 0){                printf("B = %d\n", count);                count++;                pthread_cond_signal(&js);        }        else        {            pthread_cond_wait(&os, &mutex);            printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n");        }    }        pthread_mutex_unlock(&mutex);    }    int main(void)    {        pthread_t tid1, tid2;        pthread_create(&tid2, NULL, B, NULL);        sleep(1);        pthread_create(&tid1, NULL, A, NULL);          pthread_join(tid1, NULL);        pthread_join(tid2, NULL);    return 0;    }

執行結果

上面的這個程式就是:2個條件變數對一個互斥量的操作。signal()傳送喚醒wait(),wait()之後的語句暫時不執行,直到下一次遇到wait()時,阻塞,返回執行喚醒的wait()之後的語句。

2、怎麼建立10個執行緒的開始執行和結束過程?

  利用2個條件變數和1個互斥量即可實現。

程式碼如下:

#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include<pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER;pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;void* thread_fun1(void *arg){    int i = *(int *)arg;    pthread_mutex_lock(&mutex);    printf("[%d] thread start up\n", i);    pthread_cond_wait(&cond, &mutex);    printf("[%d]thread is wake up\n", i);    pthread_mutex_unlock(&mutex);    }    void* thread_fun2(void *arg)    {        pthread_cond_broadcast(&cond); //廣播,一次喚醒所有的執行緒}int main(void){        pthread_t tid1[10], tid2;        int i;        for(i = 0; i < 10; i++)        {            pthread_create(&tid1[i], NULL, thread_fun1, &i);//建立10個執行緒            sleep(1);    }        pthread_create(&tid2, NULL, thread_fun2, NULL);//建立1個執行緒    for(i = 0; i < 10; i++)    {   //主執行緒等子執行緒執行完        pthread_join(tid1[i], NULL);    }        pthread_join(tid2, NULL);        return 0;    }

執行結果

多執行緒的程式設計中:互斥量、條件變數是重中之重!!!

喜歡本文的朋友們,歡迎長按下圖關注

訂閱號程式設計小兔崽,收看更多精彩內容

每天進步一點點,如果有用給小編點個