2013年11月10日 星期日

線程鎖的概念函數EnterCriticalSection和LeaveCriticalSection的用法

使用結構CRITICAL_SECTION 需加入頭文件#include 「afxmt.h」
定義一個全局的鎖 CRITICAL_SECTION的實例
和一個靜態全局變量
CRITICAL_SECTION cs;//可以理解為鎖定一個資源
static int n_AddValue = 0;//定義一個靜態的全部變量n_AddValue
創建兩個線程函數,代碼實現如下:
複製代碼
代碼
//第一個線程
UINT FirstThread(LPVOID lParam)
{
     EnterCriticalSection(&cs);//加鎖 接下來的代碼處理過程中不允許其他線程進行操作,除非遇到LeaveCriticalSection
     for(int i = 0; i<10; i++){       
         n_AddValue ++;
         cout << "n_AddValue in FirstThread is "<<n_AddValue <<endl;       
     }
     LeaveCriticalSection(&cs);//解鎖 到EnterCriticalSection之間代碼資源已經釋放了,其他線程可以進行操作   
     return 0;
}//第二個線程
UINT SecondThread(LPVOID lParam)
{
    EnterCriticalSection(&cs);//加鎖
    for(int i = 0; i<10; i++){       
        n_AddValue ++;       
        cout << "n_AddValue in SecondThread is "<<n_AddValue <<endl;   
       
    }
    LeaveCriticalSection(&cs);//解鎖
    return 0;
}
複製代碼

在主函數添加以下代碼
複製代碼
代碼
 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 {
    int nRetCode = 0;
 
    // 初始化 MFC 並在失敗時顯示錯誤
     if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
         // TODO: 更改錯誤代碼以符合您的需要
         _tprintf(_T("錯誤: MFC 初始化失敗\n"));
        nRetCode = 1;
     }
     else
    {
        InitializeCriticalSection(&cs);//初始化結構CRITICAL_SECTION
 
        CWinThread *pFirstThread,*pSecondThread;//存儲函數AfxBeginThread返回的CWinThread指針
       
        pFirstThread  = AfxBeginThread(FirstThread,LPVOID(NULL));//啟動第一個線程
        pSecondThread = AfxBeginThread(SecondThread,LPVOID(NULL));//啟動第二個線程
  
        HANDLE hThreadHandle[2];//        hThreadHandle[0] = pFirstThread->m_hThread;
        hThreadHandle[1] = pSecondThread->m_hThread;
 
        //等待線程返回
        WaitForMultipleObjects(2,hThreadHandle,TRUE,INFINITE);       
    }
 
    return nRetCode;
}
複製代碼

輸出:
n_AddValue in FirstThread is 1
n_AddValue in FirstThread is 2
n_AddValue in FirstThread is 3
n_AddValue in FirstThread is 4
n_AddValue in FirstThread is 5
n_AddValue in FirstThread is 6
n_AddValue in FirstThread is 7
n_AddValue in FirstThread is 8
n_AddValue in FirstThread is 9
n_AddValue in FirstThread is 10
n_AddValue in SecondThread is 11
n_AddValue in SecondThread is 12
n_AddValue in SecondThread is 13
n_AddValue in SecondThread is 14
n_AddValue in SecondThread is 15
n_AddValue in SecondThread is 16
n_AddValue in SecondThread is 17
n_AddValue in SecondThread is 18
n_AddValue in SecondThread is 19
n_AddValue in SecondThread is 20
如果把兩個線程函數中的EnterCriticalSection和LeaveCriticalSection位置移到for循環中去,線程的執行順序將會改變
輸出也就跟著改變,如:

複製代碼
代碼
//第一個線程
 UINT FirstThread(LPVOID lParam)
{
    
    for(int i = 0; i<10; i++){
        EnterCriticalSection(&cs);//加鎖 鎖移到for循環內部裡
        n_AddValue ++;
        cout << "n_AddValue in FirstThread is "<<n_AddValue <<endl;   
        LeaveCriticalSection(&cs);//解鎖 
    }   
    return 0;
}
  
 //第二個線程
UINT SecondThread(LPVOID lParam)
{
   
    for(int i = 0; i<10; i++){   
        EnterCriticalSection(&cs);//加鎖
        n_AddValue ++;       
        cout << "n_AddValue in SecondThread is "<<n_AddValue <<endl;
        LeaveCriticalSection(&cs);//解鎖       
     }
     return 0;
 }
複製代碼

其他代碼不變,輸出的結果如下:
n_AddValue in FirstThread is 1
n_AddValue in SecondThread is 2
n_AddValue in FirstThread is 3
n_AddValue in SecondThread is 4
n_AddValue in FirstThread is 5
n_AddValue in SecondThread is 6
n_AddValue in FirstThread is 7
n_AddValue in SecondThread is 8
n_AddValue in FirstThread is 9
n_AddValue in SecondThread is 10
n_AddValue in FirstThread is 11
n_AddValue in SecondThread is 12
n_AddValue in FirstThread is 13
n_AddValue in SecondThread is 14
n_AddValue in FirstThread is 15
n_AddValue in SecondThread is 16
n_AddValue in FirstThread is 17
n_AddValue in SecondThread is 18
n_AddValue in FirstThread is 19
n_AddValue in SecondThread is 20
個人認為在函數EnterCriticalSection和LeaveCriticalSection中間的代碼執行過程不會被其他線程干攏或者這麼講不允許其他線程中
的代碼執行。這樣可以有效防止一個全局變量在兩個線程中同時被操作的可能性