Linux系統編程 —互斥量mutex

互斥量mutex

前文提到,系統中若是存在資源共享,線程間存在競爭,而且沒有合理的同步機制的話,會出現數據混亂的現象。爲了實現同步機制,Linux中提供了多種方式,其中一種方式爲互斥鎖mutex(也稱之爲互斥量)。多線程

互斥量的具體實現方式爲:每一個線程在對共享資源操做前都嘗試先加鎖,成功加鎖後才能夠對共享資源進行讀寫操做,操做結束後解鎖。函數

互斥量不是爲了消除競爭,實際上,資源仍是共享的,線程間也仍是競爭的,只不過經過這種「鎖」機制就將共享資源的訪問變成互斥操做,也就是說一個線程操做這個資源時,其它線程沒法操做它,從而消除與時間有關的錯誤。人工智能

從互斥量的實現機制咱們能夠看出,同一時刻,只能有一個線程持有該鎖。若是有同時有多個線程持有該鎖,那就沒有實際意義了。操作系統

可是,這種鎖機制不是強制的,互斥鎖實質上是操做系統提供的一把「建議鎖」(又稱「協同鎖」),建議程序中有多線程訪問共享資源的時候使用該機制。線程

所以,即便有了mutex,其它線程若是不按照這種鎖機制來訪問共享數據的話,依然會形成數據混亂。因此爲了不這種狀況,全部訪問該共享資源的線程必須採用相同的鎖機制。指針

主要應用函數:rest

​ pthread_mutex_init函數
​ pthread_mutex_destroy函數
​ pthread_mutex_lock函數
​ pthread_mutex_trylock函數
​ pthread_mutex_unlock函數code

以上5個函數的返回值都是:成功返回0,失敗返回錯誤號。內存

在Linux環境下,類型pthread_mutex_t其本質是一個結構體。可是爲了簡化理解,應用時可忽略其實現細節,簡單當成整數看待。mutex通常如下面方式定義:資源

pthread_mutex_t mutex;

變量mutex只有兩種取值一、0。

pthread_mutex_init函數

函數原型:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

函數做用:

初始化一個互斥鎖(互斥量)mutex,初值可視爲1;

參數介紹:

mutex:傳出參數,調用時應傳 &mutex給該函數;

這裏有個關鍵字比較特殊:restrict。它的做用只用於限制指針,告訴編譯器,全部修改該指針指向內存中內容的操做,只能經過本指針完成。不能經過除本指針之外的其餘變量或指針修改。好比說,再定義個pthread_mutex_t的指針,將其賦值爲mutex的值,想要用它來修改mutex所指向的內存,這是不容許的。

attr:互斥量屬性。是一個傳入參數,一般傳NULL,表示使用默認屬性(即:線程間共享)。

對於互斥量mutex的初始化有兩種方式:

1. 靜態初始化:

若是互斥鎖 mutex 是靜態分配的,即:定義爲全局變量,或加了static關鍵字修飾,能夠直接使用宏進行初始化。e.g. pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;

2. 動態初始化:

若是互斥鎖mutex定義爲局部變量,則應採用動態初始化。e.g. pthread_mutex_init(&mutex, NULL)

pthread_mutex_destroy函數

函數原型:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

函數做用:

銷燬一個互斥鎖

pthread_mutex_lock函數

函數原型:

int pthread_mutex_lock(pthread_mutex_t *mutex);

函數做用:

對共享資源進行加鎖。可理解爲將mutex--(或-1);若是加鎖不成功,則該線程將阻塞,直到持有該互斥量的其餘線程解鎖爲止。注意:在訪問共享資源前加鎖,訪問結束後當即解鎖。鎖的「粒度」應越小越好。

pthread_mutex_unlock函數

函數原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

函數做用:

對共享資源解鎖。可理解爲將mutex ++(或+1);在解鎖的同時,會將阻塞在該鎖上的全部線程所有喚醒,至於哪一個線程先被喚醒,取決於優先級、調度。默認狀況下:先阻塞的線程會先被喚醒。

pthread_mutex_trylock函數

函數原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

函數做用:

對共享資源嘗試加鎖。它與pthread_mutex_lock函數的區別是,使用lock函數對共享資源進行加鎖時,若是加鎖不成功,則線程就阻塞;而若是使用trylock,則加鎖不成功時不會阻塞當前線程,而是當即返回一個值來描述互斥鎖的情況。

死鎖:

  1. 線程試圖對同一個互斥量A加鎖兩次。
  2. 線程1擁有A鎖,請求得到B鎖;線程2擁有B鎖,請求得到A鎖
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t mutex;

void *tfn(void *arg)
{
    srand(time(NULL));

    while(1) {
        pthread_mutex_lock(&mutex);
        printf("hello ");           // 標準輸出爲共享資源
        sleep(rand() % 3);          // 在此時會失去CPU
        printf("world!\n");
        pthread_mutex_unlock(&mutex);
        sleep(rand() % 3);
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    int n = 5;

    srand(time(NULL));

    pthread_mutex_init(&mutex, NULL);
    pthread_create(&tid, NULL, tfn, NULL);

    while(n--) {
        pthread_mutex_lock(&mutex);
        printf("HELLO ");
        sleep(rand() % 3);
        printf("WORLD!\n");
        pthread_mutex_unlock(&mutex);
        sleep(rand() % 3);
    }

    pthread_cancel(tid);
    pthread_join(tid, NULL);

    pthread_mutex_destroy(&mutex);
    return 0;
}

更多精彩內容,請關注公衆號良許Linux,公衆內回覆1024可免費得到5T技術資料,包括:Linux,C/C++,Python,樹莓派,嵌入式,Java,人工智能,等等。公衆號內回覆進羣,邀請您進高手如雲技術交流羣。

img


公衆號:良許Linux

有收穫?但願老鐵們來個三連擊,給更多的人看到這篇文章

相關文章
相關標籤/搜索