windows多執行緒--原子操作

tenos發表於2014-03-15

推薦參考部落格:秒殺多執行緒第三篇 原子操作 Interlocked系列函式

                   原子操作 VS 非原子操作                   

 

原子操作就是不會被執行緒排程機制打斷的操作,這種操作一旦開始,就一直執行到結束,中間不會有任何執行緒切換。                                          本文地址

首先從一個簡單的例子來看,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;
    }
}

 

image

為什麼會這樣呢,因為g_cnt++不是一個原子操作,在vs2010中檢視反彙編程式碼為:

image

對於++操作,編譯器把它分為三步: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

相關文章