1. 程式人生 > >執行緒同步----訊號量

執行緒同步----訊號量

上篇文章《執行緒同步---睡覺與喚醒》遺留了一個問題,就是會出現死鎖的情況。所以為了解決這個問題,又要引入一個新的同步概念,那就是訊號量。     起初我個人在理解的時候陷入了一個奇怪的圈子,就總憑感覺和名字認為這個訊號量是不是一個儲存我之前失效訊號的容器,讓後在需要之前訊號的時候在重新呼叫訊號。   其實沒有那麼複雜。書上一堆繞來繞去的話令剛接觸的人的確不太好想,總之先忘了上篇講到的sleep和wake吧!    以我的理解來概括訊號量的話,就是一把可以讓對方睡眠或喚醒的智慧鎖。    我們都知道之前接觸的鎖都是一個執行緒進去,把鎖上鎖了,其他程序就只能在鎖外面等著它執行完,釋放了鎖才能進入剛被鎖住的區域。而訊號量就好比把之前的鎖優化了,其他程序被鎖堵在外面會去睡覺,而不是等待!等佔有鎖的執行緒把臨界區的程式碼執行完了,會去喚醒剛去睡覺的其他執行緒。    而訊號量中這個 量 字不難理解,肯定和數量有關,其實就是個計數器。    想想以前的鎖,是不是隻有兩種情況,要麼開著,要麼鎖著。要麼計數器是0沒有鎖(鎖被人拿走了,代表已經有某個執行緒進入臨界區了),要麼計數器是1有鎖(沒人拿走鎖,代表臨界區現在沒有執行緒了。我可以把這個1拿走讓這個計數器變為0,我進入臨界區,後面來的執行緒看見計數器是0了,就明白是什麼意思睡覺去了。)  那麼結合上篇x取0-10的這個例子,我們是不是能讓這個計數器的範圍擴大一下。 上篇文章的例子:
 A執行緒負責給x++,只要x不到10的時候就必須給x加1 。如果x為10了,自己就去睡覺。. B執行緒負責給x--,只要x不為0,B就一直讓x-1。如果x為0,自己就去睡覺  對於A執行緒來說,只要計數器不為10,A都可以獲得一把鎖進入臨界區。  對於B執行緒來說,只要計數器不為0,B也都可以獲得一把鎖進入臨界區。而表示計數器加減的用up()和down()down表示只要計數器是不為0的,即≥1的情況,就可以拿一把鎖,讓計數器減1,up表示計數器不超過計數器上限的。即≤MAX-1的情況,就可以歸還一把鎖,讓計數器加1.那麼具體實現套到具體例子來看看。定義  int x=2;    //隨便給x一個初始值         int MAX=10;         int a1=2;   //x的個數         int a2=8;   //x還差多少才能滿的個數         int tmp=1;  // 相當於一把1和0的互斥鎖A:      down( a2);            //如果x距滿的個數還差 不為0個(意思就是不是滿的),就可以進入臨界區對x++。      down(tmp);               x++;      up(tmp);      up(a1);           //由於x++了。所以釋放鎖的時候應該讓  表示x個數的a1去++B:      down(a1);             //如果x的個數不為0,就可以進入下面臨界區,對x進行--      down(tmp);       x--;      up(tmp);      up(a2);    .//由於x--了,所以釋放鎖的時候應該讓  表示差多少個才能滿的a2++至於上面為什麼A和B的兩個down下面又加了一對down(tmp)/up(tmp)呢?就是防止一種情況。A和B在大部分情況都是可以同時進入臨界區的,比如就那x=4來說,x是4,a1=4不是0;x距離滿還差6個,a2=6也不是0,A和B都可以進入下面的臨界區對x進行修改,那不就又出現的無法保護臨界區資料的情況。所以要在修改x的前後加入一把鎖,這把鎖就只有0,1兩個選項,也就是說哪怕A,B同時進入臨界區了也無所謂,反正A,B倆個執行緒之中只有一個會得到tmp這把鎖。才能對x修改。 那會不會出現死鎖的情況呢,AB執行緒,無論誰先完成一步up了,cpu再怎麼切換。都會對a1或a2進行一次up。那就代表a1和a2至少有一個不是0,死鎖發生的條件是cpu的切換導致喚醒函式失去作用讓兩個執行緒同時睡著了沒人叫
。剛說了a1和a2至少有一個不為0,是不是代表至少有一個不會被down(0)判斷成功使它睡覺去。既然同時睡覺成了不可能的事情,那麼死鎖就不會發生了。(當然最起碼的條件是down和up的位置相互匹配,如果down(a1)和down(tmp)的位置交換,邏輯都不對了,死鎖產生是必然的)。