1. 程式人生 > >基本數據類型在多線程的情況下是否需要加鎖

基本數據類型在多線程的情況下是否需要加鎖

等於 access mes 程序 大於 bold data 結構 全局

對於多線程訪問同一變量是否需要加鎖的問題,先前大家都討論過。今天用代碼驗證了一下之前的猜想:32位CPU與內存的最小交換數據為4字節/次,這也是結構體要對齊4字節的原因。在物理上,CPU對於同一4字節的內存單元,不可能寫2個字節的同時,又讀了3字節。

測試環境為:

XEON 2CPU*2
Windows7

采用50,50,50線程交叉讀寫,試驗代碼如下:

C/C++ code ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 <br data-filtered="filtered"> int g_test;<br data-filtered="filtered"> int temp;<br data-filtered="filtered"> BOOL g_bRunning;<br data-filtered="filtered"> DWORD WINAPI thWriteProc1(LPVOID lParam)<br data-filtered="filtered"> {<br data-filtered="filtered">
while(g_bRunning)<br data-filtered="filtered"> {<br data-filtered="filtered"> g_test = 12345678;<br data-filtered="filtered"> Sleep(1);<br data-filtered="filtered"> }<br data-filtered="filtered"> return 0;<br data-filtered="filtered"> }<br data-filtered=
"filtered"> DWORD WINAPI thWriteProc2(LPVOID lParam)<br data-filtered="filtered"> {<br data-filtered="filtered"> while(g_bRunning)<br data-filtered="filtered"> {<br data-filtered="filtered"> g_test = 13579246;<br data-filtered="filtered"> Sleep(1);<br data-filtered="filtered"> }<br data-filtered="filtered"> return 0;<br data-filtered="filtered"> }<br data-filtered="filtered"> <br data-filtered="filtered"> DWORD WINAPI thReadProc(LPVOID lParam)<br data-filtered="filtered"> {<br data-filtered="filtered"> while(g_bRunning)<br data-filtered="filtered"> {<br data-filtered="filtered"> temp = g_test;//讀取值<br data-filtered="filtered"> if ( temp != 12345678 && temp != 13579246 )<br data-filtered="filtered"> {<br data-filtered="filtered"> g_bRunning = FALSE;<br data-filtered="filtered"> CString str;<br data-filtered="filtered"> str.Format("read error!%d", temp);<br data-filtered="filtered"> AfxMessageBox(str);<br data-filtered="filtered"> break;<br data-filtered="filtered"> }<br data-filtered="filtered"> Sleep(1);<br data-filtered="filtered"> }<br data-filtered="filtered"> return 0;<br data-filtered="filtered"> }<br data-filtered="filtered"> void CTestMultiyAccessIntDlg::OnButton1() <br data-filtered="filtered"> {<br data-filtered="filtered"> g_bRunning = TRUE;<br data-filtered="filtered"> for ( int i = 0; i < 50; i++ )<br data-filtered="filtered"> {<br data-filtered="filtered"> //創建50個寫線程1<br data-filtered="filtered"> CreateThread( NULL, 0, thWriteProc1, NULL, 0, NULL );<br data-filtered="filtered"> }<br data-filtered="filtered"> for ( int i = 0; i < 50; i++ )<br data-filtered="filtered"> {<br data-filtered="filtered"> //創建50個寫線程2<br data-filtered="filtered"> CreateThread( NULL, 0, thWriteProc2, NULL, 0, NULL );<br data-filtered="filtered"> }<br data-filtered="filtered"> for ( int i = 0; i < 50; i++ )<br data-filtered="filtered"> {<br data-filtered="filtered"> //創建50個讀線程<br data-filtered="filtered"> CreateThread( NULL, 0, thReadProc, NULL, 0, NULL );<br data-filtered="filtered"> }<br data-filtered="filtered"> }<br data-filtered="filtered">



測試方法:
改變g_test的類型,給g_test賦予不同的值(不要超過類型的上限值)

測試現象:
當g_test為int,short char時,不存在多線程交叉讀寫錯誤的問題
當g_test為double, float, __int64時,存在多線程交叉讀寫錯誤的問題,對於__int64,當賦值小於0xFFFFFFFF時不出錯,當大於0xFFFFFFFF時出錯
當g_test為CString時,存在交叉讀寫錯誤,有時候程序崩潰
另:不加Sleep(1)機器卡死過,CPU占用率達到100%,4個核心占用率全滿,可以保證運行在多核環境下

現象分析:
(1)int short char均為小於4字節的連續內存塊,CPU一條指令就可以讀寫它們的值,CPU不可能同一個時間執行兩條指令
(2)double為8字節,如果寫線程先寫了4字節,讀線程讀了8字節,這自然導致數據被破壞
(3)float也為4字節,我也不是太清楚為什麽不行,可能是VC對浮點數的處理比較特殊有關,浮點數具有復雜內存結構
(4)__int64為8字節,存在和(2)相同的情況,如果__int64小於等於0xFFFFFFFF,相當於只改變了低4字節,因此就沒有問題
(5)CString為類類型,具有復雜結構,顯然不行

結論:
1.對於int,short,char,BOOL等小於等於4字節的簡單數據類型,如果無邏輯上的先後關系,多線程讀寫可以完全不用加鎖
2.盡管float為4字節,多線程訪問時也需要加鎖
3.對於大於4字節的簡單類型,比如double,__int64等,多線程讀寫必須加鎖。
4.對於所有復雜類型,比如類,結構體,容器等類型必須加鎖

盡管對int等類型的多線程讀寫不需要加鎖,但是邏輯上有必要加鎖的還是應該加鎖
例如:對於一個多線程訪問的全局變量int g_test
int count = g_test/1024;
int mod = g_test%1024;
由於是兩條語句,執行完第一條之後,別的線程很可能已經修改了g_test的值,如果希望這兩條語句執行時,g_test不發生變化,就必須加鎖,以保證兩條語句執行的整體性。
Lock();
int count = g_test/1024;
int mod= g_test%1024;
UnLock();
如果不加鎖,也可以改為先保存到一個臨時變量裏
int temp = g_test;
int count = temp/1024;
int mod = temp%1024;

基本數據類型在多線程的情況下是否需要加鎖