1. 程式人生 > >c++多執行緒重點難點(一)interlocked系列原子操作

c++多執行緒重點難點(一)interlocked系列原子操作

_beginthreadex()函式在建立新執行緒時會分配並初始化一個_tiddata塊。這個_tiddata塊自然是用來存放一些需要執行緒獨享的資料。事實上新執行緒執行時會首先將_tiddata塊與自己進一步關聯起來。然後新執行緒呼叫標準C執行庫函式如strtok()時就會先取得_tiddata塊的地址再將需要保護的資料存入_tiddata塊中。這樣每個執行緒就只會訪問和修改自己的資料而不會去篡改其它執行緒的資料了。因此,如果在程式碼中有使用標準C執行庫中的函式時,儘量使用_beginthreadex()來代替CreateThread()。

//子執行緒報數
#include <stdio.h>
#include <process.h> #include <windows.h> int g_nCount; //子執行緒函式 unsigned int __stdcall ThreadFun(PVOID pM) { g_nCount++; printf("執行緒ID號為%4d的子執行緒報數%d\n", GetCurrentThreadId(), g_nCount); return 0; } //主函式,所謂主函式其實就是主執行緒執行的函式。 int main() { printf(" 子執行緒報數 \n"); printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n"
); const int THREAD_NUM = 10; HANDLE handle[THREAD_NUM]; g_nCount = 0; for (int i = 0; i < THREAD_NUM; i++) handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL); WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); return 0; }

以上程式碼是不能實現按數字順序輸出結果的,即執行緒不同步不互斥

這樣由於執行緒執行的併發性,執行緒A執行到第二次從暫存器裡取出第一次的終值,這時候執行緒B開始執行,取出的也是A第一次的終值,執行緒B完成後,暫存器儲存,然後執行緒A也完成,又儲存一次,相當於覆蓋了執行緒B的終值。這樣執行下來,結果是不可預知的——可能會出現50,可能小於50。

因此在多執行緒環境中對一個變數進行讀寫時,我們需要有一種方法能夠保證對一個值的遞增操作是原子操作——即不可打斷性,一個執行緒在執行原子操作時,其它執行緒必須等待它完成之後才能開始執行該原子操作。這種涉及到硬體的操作會不會很複雜了,幸運的是,Windows系統為我們提供了一些以Interlocked開頭的函式來完成這一任務

#include <stdio.h>
#include <windows.h>
volatile long g_nLoginCount; //登入次數
unsigned int __stdcall Fun(void *pPM); //執行緒函式
const DWORD THREAD_NUM = 50;//啟動執行緒數
DWORD WINAPI ThreadFun(void *pPM)
{
    Sleep(100);//some work should to do
    //g_nLoginCount++;
    InterlockedIncrement((LPLONG)&g_nLoginCount);
    Sleep(50);
    return 0;
}
int main()
{
    printf("     原子操作 Interlocked系列函式的使用\n");
    printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

    //重複20次以便觀察多執行緒訪問同一資源時導致的衝突
    int num= 20;
    while (num--)
    {   
        g_nLoginCount = 0;
        int i;
        HANDLE  handle[THREAD_NUM];
        for (i = 0; i < THREAD_NUM; i++)
            handle[i] = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
        WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
        printf("有%d個使用者登入後記錄結果是%d\n", THREAD_NUM, g_nLoginCount);
    }
    return 0;
}