1. 程式人生 > >多執行緒的那點兒事 之資料同步

多執行緒的那點兒事 之資料同步

               

【 宣告:版權所有,歡迎轉載,請勿用於商業用途。  聯絡信箱:feixiaoxing @163.com】

    多執行緒建立其實十分簡單,在windows系統下面有很多函式可以建立多執行緒,比如說_beginthread。我們就可以利用它為我們編寫一段簡單的多執行緒程式碼,

 #include <windows.h> #include <process.h> #include <stdio.h>  unsigned int value = 0;  void print(void* argv) {  while(1){   printf("&value = %x, value = %d\n"
, &value, value);   value ++;   Sleep(1000);  } }  int main() {     _beginthread( print, 0, NULL );  _beginthread( print, 0, NULL);   while(1)    Sleep(0);   return 1; }

    注意,在VC上面編譯的時候,需要開啟/MD開關。具體操作為,【project->setting->c/c++->CategoryCode Generation->Use run-time library->Debug Multithreaded

】即可。

    通過上面的示例,我們看到作為共享變數的value事實上是可以被所有的執行緒訪問的。這就是執行緒資料同步的最大優勢——方便,直接。因為執行緒之間除了堆疊空間不一樣之外,程式碼段和資料段都是在一個空間裡面的。所以,執行緒想訪問公共資料,就可以訪問公共資料,沒有任何的限制。

    當然,事物都有其兩面性。這種對公共資源的訪問模式也會導致一些問題。什麼問題呢?我們看了就知道了。

    現在假設有一個池塘,我們僱兩個人來餵魚。兩個人不停地對池塘裡面的魚進行餵食。我們規定在一個人餵魚的時候,另外一個人不需要再餵魚,否則魚一次喂兩回就要撐死了。為此,我們安裝了一個牌子作為警示。如果一個人在餵魚,他會把牌子設定為FALSE

,那麼另外一個人看到這個牌子,就不會繼續餵魚了。等到這個人喂完後,他再把牌子繼續設定為TRUE

    如果我們需要把這個故事寫成程式碼,那麼怎麼寫呢?朋友們試試看,

 while(1){  if( flag == true){         flag = false;   do_give_fish_food();   flag = true;  }   Sleep(0); }

    上面的程式碼看上去沒有問題了,但是大家看看程式碼的彙編程式碼,看看是不是存在隱患。因為還會出現兩個人同時餵食的情況,

23:       while(1){004010E8   mov         eax,1004010ED   test        eax,eax004010EF   je          do_action+56h (00401126)24:           if( flag == true){004010F1   cmp         dword ptr [flag (00433e04)],1004010F8   jne         do_action+43h (00401113)25:               flag = false;004010FA   mov         dword ptr [flag (00433e04)],026:               do_give_fish_food();00401104   call        @ILT+15(do_give_fish_food) (00401014)27:               flag = true;00401109   mov         dword ptr [flag (00433e04)],128:           }29:30:           Sleep(0);00401113   mov         esi,esp00401115   push        000401117   call        dword ptr [[email protected]4 (004361c4)]0040111D   cmp         esi,esp0040111F   call        __chkesp (004011e0)31:       }00401124   jmp         do_action+18h (004010e8)32:   }

    我們此時假設有兩個執行緒ab在不停地進行判斷和餵食操作。設定當前flag = true,此時執行緒a執行到004010F8處時,判斷魚還沒有餵食,正準備執行指令004010F8,但是還沒有來得及對falg進行設定,此時出現了執行緒排程。執行緒b執行到004010F8時,發現當前沒有人餵食,所以執行餵食操作。等到b執行緒餵食結束,執行到00401113的時候,此時又出現了排程。執行緒a有繼續執行,因為之前已經判斷了當前還沒有餵食,所以執行緒a繼續進行了餵食了操作。所以,可憐的魚,這一次就連續經歷了兩次餵食操作,估計有一部分魚要撐死了。

    當然魚在這裡之所以會出現撐死的情況,主要是因為line 24line 25之間出現了系統排程。所以,我們在編寫程式的時候必須有一個牢固的思想意識,如果缺少必須要的手段,程式可以任何時刻任何地點被排程,那此時公共資料的計算就會出現錯誤。

    那麼有沒有方法避免這種情況的發生呢?當然有。朋友們可以繼續關注下面的部落格。