本篇我記錄一下自己學習互斥鎖的成果。
線程同步的概述:
當線程A在對一個共享資源執行寫操作時,如果此時線程B恰好也對這個共享資源執行寫操作,那麼在進程中就會出現兩個線程同時對這個共享資源進行寫操作,會出現什麼結果呢?當然是會破壞我們寫入的數據,造成不可預計的錯誤。
而使用互斥鎖(mutex)就正好可以解決這一問題,當線程A在操作(讀/寫)共享資源時,對其進行加鎖,此時如果線程B也要對這個共享資源進行操作時,由於這個鎖的存在會線程B被阻塞,直到線程A釋放掉它之前加在這個共享資源上的鎖時,線程B纔會變成可運行狀態。
我使用僞代碼來表示:
lock(&mutex);
訪問共享資源
unlock(&mutex);
其本質並不是把數據“鎖住”,而是爲線程提供一種串行化執行的機制(讓不同的線程避免在同一時間去操作數據),我曾做過一個驗證,fun1和fun2是供不同線程執行的程序,如果我註銷掉fun2的鎖,並且在fun1鎖住後不釋放,那麼在線程調用fun2的時候它依舊會操作到數據。
正常情況下,如果fun1和fun2遵循我們的互斥鎖約定,當其中任何一個線程獲得互斥鎖後,其他試圖獲得這個鎖的線程都會被阻塞,這樣就巧妙的將兩個線程分別安排在不同的時段執行,保證了任何時刻下只有唯一的線程有“權利”去操作這個共享資源。(我一開始也是誤解了互斥鎖的真正含義,後來經過多次實例驗證才知道這點)
代碼如下:
…
int num = 3;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_t TID1, TID2;
/*線程調用函數*/
void *fun1(void *arg)
{
int ret;
ret =pthread_mutex_lock(&lock);
if (0 != ret)
{
printf("pthreada lock error\n");
return ((void *)-1);
}
num = num + 1;
printf("in thepthread A num is %d\n", num);
while(1);
//pthread_mutex_unlock(&lock);
return ((void *)-1);
}
/*線程調用函數*/
void *fun2(void *arg)
{
int ret;
//ret =pthread_mutex_lock(&lock);
if (0 != ret)
{
printf("pthreadb lock error\n");
return ((void *)-1);
}
int num = 77;
printf("in thepthread B num is %d\n", num);
//pthread_mutex_unlock(&lock);
return ((void *)-1);
}
互斥鎖的初始化:
互斥鎖是使用 pthread_mutex_t數據類型來表示的。
在使用互斥鎖之前要對其進行初始化。
- 靜態分配
Eg.
Pthread_mutex_tlock = PTHREAD_MUTEX_INITIALIZER
- 動態分配
Eg.
Structfoo
{
intf_count;
pthread_mutex_tf_lock’
intfid;
…
}
Structfoo *fp;
Fp= malloc(sizeof(struct foo));
Pthread_mutex_init(&fp->f_lock,&NULL);
…
Pthread_mutex_destory(&fp->f_lock);
Free(fp);
上面可以看到,
在動態初始化的時候,用到了如下兩個函數:
- 互斥鎖初始化:
在動態初始化之前要申請好內存
intpthread_mutex_init(pthread_mutex_t *restrict mutex,
constpthread_mutexattr_t *restrict attr);
- 銷燬互斥鎖
在銷燬之前要釋放動態申請的內存
intpthread_mutex_destory(pthread_mutex_t *mutex);
互斥鎖的加鎖&解鎖:
- 對互斥鎖進行加鎖
int pthread_mutex_lock(pthread_mutex_t*mutex);
返回值:若成功,返回0;否則返回錯誤編號
線程A調用該函數加鎖以後,如果線程B再調用這個函數對線程A加過鎖的互斥鎖進行加鎖,線程B將被阻塞直到線程A釋放這個鎖。
- 嘗試對互斥鎖進行加鎖
intpthread_mutex_trylock(pthread_mutex_t *mutex);
返回值:若成功,返回0;否則返回錯誤編號
如果調用該函數時,互斥鎖處於未解鎖狀態,那麼pthread_mutex_trylock將會鎖住互斥鎖,不會出現阻塞,直接返回0,否則就會失敗,並返回EBUSY
- 對互斥鎖進行解鎖
intpthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:若成功,返回0;否則返回錯誤編號
避免死鎖:
- 如果線程試圖對同一個互斥鎖加鎖兩次,那麼他就會陷入死鎖狀態;
- 線程A一直佔着 互斥鎖1 ,這時它試圖對互斥鎖2 進行加鎖,
線程B此時佔着 互斥鎖2,此時它也試圖對 互斥鎖1 進行加鎖;
這種情況下就出現了死鎖的場景。
避免這種死鎖的方式:
1. 線程B可以先釋放自己目前佔有的鎖,過一段時間再去嘗試加鎖;
2. 讓兩個線程以相同的順序加鎖,這樣可以避免死鎖
互斥鎖的屬性:
互斥鎖屬性是用pthread_mutexattr_t結構表示的,對於非默認屬性可以使用如下倆個函數來初始化和反初始化:
intpthread_mutexattr_init(pthread_mutexattr_t *attr);
lint pthread_mutexattr_destroy(pthread_mutexattr_t*attr);
兩個函數的返回值:若成功,返回0;若失敗,返回錯誤編號
共享屬性:
該屬性有兩種情況:
- PTHREAD_PROCESS_PRIVATE 這種是默認情況,表示互斥鎖只能在本進程中使用
- PTHREAD_PROCESS_SHARED 表示互斥鎖可以在不同進程間使用
使用如下兩個函數來設置:
1. int pthread_mutexattr_getpshared(constpthread_mutexattr_t *restrict attr,int *restrict pshared);
2. intpthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared);
返回值:若成功,返回0;若失敗,返回錯誤編碼