Linux--線程的同步與互斥

一、 mutex互斥量

   同步:就是對資源的訪問有序。互斥:就是任一時刻來說只有一個在執行;但是對於多線程的程序來說,訪問衝突的問題是很普遍的,解決的辦法是引入互斥鎖(Mutex,MutualExclusive Lock),獲得鎖的線程可以完成“讀-修改-寫”的操作,然後釋放鎖給其它線程,沒有獲得鎖的線程只能等待而不能訪問共享數據,這樣“讀-修改-寫”三步操作組成一個原子操作,要麼都執行,要麼都不執行,不會執行到中間被打斷,也不會在其它處理器上並行做這個操作。

   互斥鎖用pthread_mutex_t類型的變量表示。用pthread_mutex_init初始化,用hread_destory()銷燬。成功返回0,失敗返回錯誤號。。如果Mutex變量是靜態分配的(全局變量 或static變量),也可以用宏定義PTHREAD_MUTEX_INITIALIZER來初始化,相當於用pthread_mutex_init初始化並且attr參數爲NULL

   一個線程可以調用pthread_mutex_lock獲得Mutex,如果這時另一個線程已經調pthread_mutex_lock獲得了該Mutex,則當前線程需要掛起等待,直到另一個線程調用pthread_mutex_unlock釋放Mutex,當前線程被喚醒,才能獲得該Mutex並繼續執行。意思就是說如果有一個線程對mutex上了鎖,沒有開鎖,另外一個線程想獲得mutex,就得掛機等待,直到上鎖的線程開了鎖釋放開mutex之後,該線程被喚醒,才能獲得mutex.

   如果一個線程既想獲得鎖,又不想掛起等待,可以調用pthread_mutex_trylock,如果Mutex已經被另一個線程獲得,這個函數會失敗返回EBUSY,而不會使線程掛起等待。

    

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 static int g_count=0;
  5 void * addWrite(void * arg)
  6 {
  7     int count=0;
  8     int value=0;
  9     while(count++ <5000)
 10     {
 11         value=g_count;
 12         printf("g_count is %d\n",g_count);
 13         g_count=value+1;
 14     }
 15 }
 16 int main()
 17 {
 18     pthread_t id1;
 19     pthread_t id2;
 20     int ret=pthread_create(&id1,NULL,addWrite,NULL);
 21     int res=pthread_create(&id2,NULL,addWrite,NULL);
 22     pthread_join(id1,NULL);
 23     pthread_join(id2,NULL);

    

我們創建兩個線程,各自把g_count增加5000次,正常情況下最後counter應該等於10000,但事實上每次運行該程序的結果都不一樣,有時候數到5000多,有時候數到6000多但是加上鎖之後(在)第五行加上pthread_mutex_init 如下圖

wKioL1cTTIPj_vooAAGGy0TV8xs368.jpg

結果:

wKioL1cTTOOS1iaZAAGPtBKpZNA377.jpg

加上鎖之後,就輸出10000

二、lock和unlock的實現原理

    爲了實現互斥鎖操作,大多數體系結構都提供了swap或exchange指令,該指令的作用是把寄存器和內存單元的數據相交換,由於只有一條指令,保證了原子性,即使是多處理器平臺,訪問內存的總線週期也有先後,一個處理器上的交換指令執行時另一個處理器的交換指令只能等待總線週期。如下圖的僞代碼所示。unlock中的釋放鎖操作同樣只用一條指令實現,以保證它的原子性。

wKioL1cTUnbgI4-UAACscmPnqN4295.jpg

三、死鎖

·一般情況下,如果同一個線程先後兩次調用lock,在第二次調用時,由於鎖已經被佔用,該線程會掛起等待別的線程釋放鎖,然而鎖正是被自己佔用着的,該線程又被掛起而沒有機會釋放鎖,因此 就永遠處於掛起等待狀態了,這叫做死鎖(Deadlock)。另一種典型的死鎖情形是這樣:線程A獲得了鎖1,線程B獲得了鎖2,這時線程A調用lock試圖獲得鎖2,結果是需要掛起等待線程B釋放鎖2,而這時線程B也調用lock試圖獲得鎖1,結果是需要掛起等待線程A釋放鎖1,於是線程A和B都永遠處於掛起狀態了。

死鎖形成的條件

    ①、互斥條件:一個資源每次只能被一個線程使用。

    ②、請求與保持條件:一個進程因請求資源而被阻塞時,對已獲得的資源保持不放。

    ③、不剝奪條件:進程已獲得的資源,在未使用完之前,不能強行剝奪。

    ④循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係

  如果涉及到更多的線程和更多的鎖,則更容易引起死鎖問題。寫程序時應該儘量避免同時獲得多個鎖,如果一定有必要這麼做,則有一個原則:如果所有線程在需要多個鎖時都按相同的先後順序(常見的是按Mutex變量的地址順序)獲得鎖,則不會出現死鎖。比如一個程序中用到鎖1、鎖2、鎖3,它們所對應的Mutex變量的地址是鎖1<鎖2<鎖3,那麼 所有線程在需要同時獲得2個或3個鎖時都應該按鎖1、鎖2、鎖3的順序獲得。如果要爲所有的鎖確定一個先後順序比較困難,則應該儘量使用pthread_mutex_trylock調用代替pthread_mutex_lock調用,以免死鎖。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章