推薦參考部落格:秒殺多執行緒第三篇 原子操作 Interlocked系列函式
原子操作就是不會被執行緒排程機制打斷的操作,這種操作一旦開始,就一直執行到結束,中間不會有任何執行緒切換。 本文地址
首先從一個簡單的例子來看,1000個執行緒同時對一個全域性變數(初始化為0)做++操作,最後我們期望的這個變數的值是1000,但是有時候結果卻事與願違:
#include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; volatile int g_cnt; unsigned __stdcall threadFun(void *param) { g_cnt++; return 0; } int main() { for(int j = 0; j < 100; j++) { g_cnt = 0; const int threadNum = 1000; HANDLE hth[threadNum]; for(int i = 0; i < threadNum; i++) hth[i] = (HANDLE)_beginthreadex(NULL, 0, threadFun, NULL, 0, NULL); //注意WaitForMultipleObjects每次最多等待MAXIMUM_WAIT_OBJECTS個object; //也可以呼叫1000次WaitForSingleObject int k = threadNum / MAXIMUM_WAIT_OBJECTS; for(int i = 0; i < k; i++) WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, &hth[i*MAXIMUM_WAIT_OBJECTS], TRUE, INFINITE); if(threadNum % MAXIMUM_WAIT_OBJECTS != 0) WaitForMultipleObjects(threadNum % MAXIMUM_WAIT_OBJECTS, &hth[k*MAXIMUM_WAIT_OBJECTS], TRUE, INFINITE); for(int i = 0; i < threadNum; i++) CloseHandle(hth[i]); if(g_cnt != 1000) cout<<"the value of g_cnt: "<<g_cnt<<endl; } }
為什麼會這樣呢,因為g_cnt++不是一個原子操作,在vs2010中檢視反彙編程式碼為:
對於++操作,編譯器把它分為三步:1、從記憶體中吧g_cnt的值讀到暫存器eax,2、eax中的值+1, 3、把eax中的值寫會記憶體
如果有兩個執行緒,執行緒1執行到第二條語句時,執行緒2開始執行,那麼執行緒2獲取的g_cnt的值還是原來的0(因為執行緒1還沒有執行低三條語句來寫回記憶體),最後g_cnt的值就是1,而不是期望的2;
windows系統提供給了一些函式來保證某些操作的原子性:
LONG __cdecl InterlockedIncrement(LONG volatile* Addend); //變數加1
LONG __cdecl InterlockedDecrement(LONG volatile* Addend);//變數減1
LONG __cdec InterlockedExchangeAdd(LONG volatile* Addend, LONG Value);//變數加上value
LONG __cdecl InterlockedExchange(LONG volatile* Target, LONG Value);//將value的值 賦值 給target指向的變數
以上列出的函式是針對32位的LONG資料的,如果是64位的資料,有其對應的函式,具體可以參考 msdn 列出的所有原子操作函式
針對上面的問題,我們可以把g_cnt++; 改為 InterlockedIncrement((LONG volatile *)&g_cnt);
【版權宣告】轉載請註明出處:http://www.cnblogs.com/TenosDoIt/p/3602426.html