多執行緒的那點兒事 之資料同步
【 宣告:版權所有,歡迎轉載,請勿用於商業用途。 聯絡信箱: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++】->Category【Code Generation】->【Use run-time library】->【Debug Multithreaded
通過上面的示例,我們看到作為共享變數的value事實上是可以被所有的執行緒訪問的。這就是執行緒資料同步的最大優勢——方便,直接。因為執行緒之間除了堆疊空間不一樣之外,程式碼段和資料段都是在一個空間裡面的。所以,執行緒想訪問公共資料,就可以訪問公共資料,沒有任何的限制。
當然,事物都有其兩面性。這種對公共資源的訪問模式也會導致一些問題。什麼問題呢?我們看了就知道了。
現在假設有一個池塘,我們僱兩個人來餵魚。兩個人不停地對池塘裡面的魚進行餵食。我們規定在一個人餵魚的時候,另外一個人不需要再餵魚,否則魚一次喂兩回就要撐死了。為此,我們安裝了一個牌子作為警示。如果一個人在餵魚,他會把牌子設定為FALSE
如果我們需要把這個故事寫成程式碼,那麼怎麼寫呢?朋友們試試看,
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: }
我們此時假設有兩個執行緒a和b在不停地進行判斷和餵食操作。設定當前flag = true,此時執行緒a執行到004010F8處時,判斷魚還沒有餵食,正準備執行指令004010F8,但是還沒有來得及對falg進行設定,此時出現了執行緒排程。執行緒b執行到004010F8時,發現當前沒有人餵食,所以執行餵食操作。等到b執行緒餵食結束,執行到00401113的時候,此時又出現了排程。執行緒a有繼續執行,因為之前已經判斷了當前還沒有餵食,所以執行緒a繼續進行了餵食了操作。所以,可憐的魚,這一次就連續經歷了兩次餵食操作,估計有一部分魚要撐死了。
當然魚在這裡之所以會出現撐死的情況,主要是因為line 24和line 25之間出現了系統排程。所以,我們在編寫程式的時候必須有一個牢固的思想意識,如果缺少必須要的手段,程式可以任何時刻任何地點被排程,那此時公共資料的計算就會出現錯誤。
那麼有沒有方法避免這種情況的發生呢?當然有。朋友們可以繼續關注下面的部落格。