互斥量mutex

Linux中提供一把互斥鎖mutex(也稱之為互斥量)。

每個線程在對資源操作前都嘗試先加鎖,成功加鎖才能操作,操作結(jié)束解鎖。
資源還是共享的,線程間也還是競爭的,但通過“鎖”就將資源的訪問變成互斥操作,而后與時間有關(guān)的錯誤也不會再產(chǎn)生了。


mutex.png

但,應(yīng)注意:同一時刻,只能有一個線程持有該鎖。
當(dāng)A線程對某個全局變量加鎖訪問,B在訪問前嘗試加鎖,拿不到鎖,B阻塞。C線程不去加鎖,而直接訪問該全局變量,依然能夠訪問,但會出現(xiàn)數(shù)據(jù)混亂。
所以,互斥鎖實質(zhì)上是操作系統(tǒng)提供的一把“建議鎖”(又稱“協(xié)同鎖”),建議程序中有多線程訪問共享資源的時候使用該機制。但,并沒有強制限定。
因此,即使有了mutex,如果有線程不按規(guī)則來訪問數(shù)據(jù),依然會造成數(shù)據(jù)混亂。

主要應(yīng)用函數(shù):

pthread_mutex_init
pthread_mutex_destroy
pthread_mutex_lock
pthread_mutex_trylock
pthread_mutex_unlock

以上5個函數(shù)的返回值都是:成功返回0, 失敗返回錯誤號。
pthread_mutex_t 類型,其本質(zhì)是一個結(jié)構(gòu)體。為簡化理解,應(yīng)用時可忽略其實現(xiàn)細(xì)節(jié),簡單當(dāng)成整數(shù)看待。
pthread_mutex_t mutex; 變量mutex只有兩種取值1、0。

pthread_mutex_init函數(shù)

初始化一個互斥鎖(互斥量) ---> 初值可看作1

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

參1:傳出參數(shù),調(diào)用時應(yīng)傳 &mutex
restrict關(guān)鍵字:只用于限制指針,告訴編譯器,所有修改該指針指向內(nèi)存中內(nèi)容的操作,只能通過本指針完成。不能通過除本指針以外的其他變量或指針修改
參2:互斥量屬性。是一個傳入?yún)?shù),通常傳NULL,選用默認(rèn)屬性(線程間共享)。 參APUE.12.4同步屬性

pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
  1. 靜態(tài)初始化:如果互斥鎖 mutex 是靜態(tài)分配的(定義在全局,或加了static關(guān)鍵字修飾),可以直接使用宏進行初始化。
pthread_mutex_init(&mutex, NULL)
  1. 動態(tài)初始化:局部變量應(yīng)采用動態(tài)初始化。
pthread_mutex_destroy函數(shù)

銷毀一個互斥鎖

int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_lock函數(shù)

加鎖??衫斫鉃閷utex--(或-1)

int pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock函數(shù)

解鎖??衫斫鉃閷utex ++(或+1)

int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_trylock函數(shù)

嘗試加鎖

int pthread_mutex_trylock(pthread_mutex_t *mutex);
加鎖與解鎖

lock與unlock:
lock嘗試加鎖,如果加鎖不成功,線程阻塞,阻塞到持有該互斥量的其他線程解鎖為止。
unlock主動解鎖函數(shù),同時將阻塞在該鎖上的所有線程全部喚醒,至于哪個線程先被喚醒,取決于優(yōu)先級、調(diào)度。默認(rèn):先阻塞、先喚醒。
例如:T1 T2 T3 T4 使用一把mutex鎖。T1加鎖成功,其他線程均阻塞,直至T1解鎖。T1解鎖后,T2 T3 T4均被喚醒,并自動再次嘗試加鎖。
可假想mutex鎖 init成功初值為1。 lock 功能是將mutex--。 unlock將mutex++
lock與trylock:
lock加鎖失敗會阻塞,等待鎖釋放。
trylock加鎖失敗直接返回錯誤號(如:EBUSY),不阻塞。

加鎖步驟測試:

看如下程序:該程序是非常典型的,由于共享、競爭而沒有加任何同步機制,導(dǎo)致產(chǎn)生于時間有關(guān)的錯誤,造成數(shù)據(jù)混亂:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

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

        printf("hello ");
        sleep(rand() % 3);  /*模擬長時間操作共享資源,導(dǎo)致cpu易主,產(chǎn)生與時間有關(guān)的錯誤*/
        printf("world\n");
        sleep(rand() % 3);
    }
    return NULL;
}
int main(void)
{
    pthread_t tid;
    srand(time(NULL));
    pthread_create(&tid, NULL, tfn, NULL);
    while (1) 
    {
        printf("HELLO ");
        sleep(rand() % 3);
        printf("WORLD\n");
        sleep(rand() % 3);
    }
    pthread_join(tid, NULL);
    return 0;
}                       
  1. 定義全局互斥量,初始化init(&m, NULL)互斥量,添加對應(yīng)的destry
  2. 兩個線程while中,兩次printf前后,分別加lock和unlock
  3. 將unlock挪至第二個sleep后,發(fā)現(xiàn)交替現(xiàn)象很難出現(xiàn)。
    線程在操作完共享資源后本應(yīng)該立即解鎖,但修改后,線程抱著鎖睡眠。睡醒解鎖后又立即加鎖,這兩個庫函數(shù)本身不會阻塞。
    所以在這兩行代碼之間失去cpu的概率很小。因此,另外一個線程很難得到加鎖的機會。
  4. main 中加flag = 5 將flg在while中-- 這時,主線程輸出5次后試圖銷毀鎖,但子線程未將鎖釋放,無法完成。
  5. main 中加pthread_cancel()將子線程取消。
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int sum=0;

void *thr1(void *arg)
{
    while(1)
    {
        //先上鎖
        pthread_mutex_lock(&mutex);//加鎖當(dāng)有線程已經(jīng)加鎖的時候會阻塞
        printf("hello");
        sleep(rand()%3);
        printf("world\n");
        //釋放鎖
        pthread_mutex_unlock(&mutex);
        sleep(rand()%3);
    }
}

void *thr2(void *arg)
{
    while(1)
    {

        pthread_mutex_lock(&mutex);
        printf("HELLO");
        sleep(rand()%3);
        printf("WORLD\n");

        pthread_mutex_unlock(&mutex);
        sleep(rand()%3);
    }
}


int main()
{
    pthread_t tid[2];
    pthread_create(&tid[0],NULL,thr1,NULL);
    pthread_create(&tid[1],NULL,thr2,NULL);

    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    return 0;
}
結(jié)論:

在訪問共享資源前加鎖,訪問結(jié)束后立即解鎖。鎖的“粒度”應(yīng)越小越好。

實例程序:

https://github.com/963375877/threadsynchronization

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容