線程同步----睡覺與喚醒

從上一篇文章《線程同步----鎖》中我們瞭解瞭解決線程同步中最基本的一些問題,那就是如何用 鎖 來保護合作線程們的臨界區數據不被重複修改。但是從這裏引出了一個問題,那就是如果一個線程A先進入了臨界區且上了鎖,等到B線程也到了臨界區前發現鎖是鎖的,那麼B只能等待直到A線程執行完臨界區代碼才能繼續往下執行,這樣就會導致cpu被浪費的佔用,因爲B沒有做任何事情,只是等待。
 因此爲了提高系統的cpu利用率。有了睡覺和喚醒這兩種操作,當一個線程B需要等待線程A時,將進入睡眠狀態。當A做了該做的事情後,將喚醒B 。A如果需要等待B做一些操作的話,也可以進行睡覺,到時候再被B喚醒。

  簡單舉個例子吧。假如依舊是x,我需要它始終處於一個範圍,那就是0-10之間。
 A線程負責給x++,只要x不到10的時候就必須給x加1 。如果x爲10了,自己就去睡覺。.
 B線程負責給x--,只要x不爲0,B就一直讓x-1。如果x爲0,自己就去睡覺。

僞代碼
int x=5;
A: 
   while(1)
    if(x==10)                
                                //flag2
         sleep();
   
          x++;
                                 //flag1
    if(x==1)             //如果x爲1則代表x++之前x是0,可能是B-1後導致的,B就可能會睡覺。
        wake(B);

}

B:
  while(1)
{
   if(x==0)
    sleep();
    x--;
   if(x==9)        //如果x爲9則代表x--之前x是10,可能是A+1後導致的,A就可能會睡覺。
     wake(A);

但以上的睡眠和喚醒會導致幾個問題。
1.使用x時都沒有給它加鎖,這就會導致線程切換的時候出現對x的誤,flag1的位置如果x++後爲2,那是不是意味着不滿足下面if(x==1)的條件,不用喚醒B,可是flag1處發生了線程切換,B線程讓x--了,x變爲1了,此時再又切換回了A線程,A線程是不是就會執行它本不應該執行的wake(B),然而B現在並沒有在睡眠wake(B)這局話不起任何作用。這個倒不會導致什麼嚴重後果,出現這個因爲沒有給x加鎖導致的,但是開頭也說了,使用睡眠和喚醒這種方式就是爲了改正上鎖後等待的線程佔用cpu的問題,再加鎖豈不是本末倒置了。其實也未必,相比與給A,B線程全局加鎖來說,只對一些影響結果的操作上鎖等待的時間非常短,可以忽略不計。我們可以在x++前和wake後 加入鎖優化這個問題。

2.出現了死鎖,導致A,B線程都睡死了,沒人叫了。flag2的位置,A線程判斷x已經爲10了,要去睡覺了,可睡之前切換到了B, B讓x--變爲9,判斷x==9正確,B想着該叫A起來了,可是A現在本來就清醒着。B這一wake(A)就沒有用,然後切換回A,A執行了sleep就睡過去了,然後之後就是B一直讓x--,直到x爲0,自己也睡去了,再也沒有條件可以時B去叫醒A了,然後這倆貨一直睡,程序死了。
出現這個問題的原因是什麼。是不是因爲B發的WAKE(A)這個信號在不該執行的時候執行了,因此失去作用了。
這種問題我們可以加鎖嗎?顯然不能的。問題1爲什麼可以加鎖,因爲wake一下就結束了,不會持續,會立刻釋放鎖;但是問題2這種死鎖問題,給A上了鎖,A如果執行sleep要一直睡,永遠釋放不了鎖,B線程還怎麼愉快的玩耍?不照樣還是死!
所以爲了解決這個問題,又有了信號量這種概念。這個我們下一篇博客再說。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章