Event 事件(C++)

背景

當創建一個線程時,其實那個線程是一個循環。這樣就帶來了一個問題,在那個死循環裏要找到合適的條件退出那個死循環,那麼是怎麼樣實現它的呢?在Windows裏往往是採用事件的方式,當然還可以採用其它的方式。在這裏先介紹採用事件的方式來通知從線程運行函數退出來,它的實現原理是:在那個死循環裏不斷地使用Wait(poco庫裏的wait函數)函數來檢查事件是否滿足,如果滿足就退出線程,不滿足就繼續運行。
通過函數SetEvent/ResetEvent分別將EVENT設置爲發信號與不發信號。
wait函數在事件所指定的對象(該對象可以是類,也可以是事件本身)爲有信號狀態時才繼續運行。

CreateEvent

創建事件函數CreateEvent,原型如下:

HANDLE CreateEvent(

  LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性

  BOOL bManualReset, // 復位方式,1爲手動復位,0爲自動復位

  BOOL bInitialState, // 初始狀態,0爲不可用狀態即無信號,1爲可用狀態即有信號

  LPCTSTR lpName // 對象名稱

  );

【參數】

  • lpEventAttributes
    一個指向SECURITY_ATTRIBUTES結構的指針,確定返回的句柄是否可被子進程繼承。如果lpEventAttributes是NULL,此句柄不能被繼承;
  • bManualReset
    指定將事件對象創建成手動復原還是自動復原。如果是TRUE,那麼必須ResetEvent函數來手工將事件的狀態復原到無信號狀態。如果設置爲FALSE,當事件被一個等待線程釋放以後,系統將會自動將事件狀態復原爲無信號狀態;
  • bInitialState
    指定事件對象的初始狀態。如果爲TRUE,初始狀態爲有信號狀態;否則爲無信號狀態;
  • lpName
    指定事件的對象的名稱,是一個以0結束的字符串指針。名稱的字符格式限定在MAX_PATH之內。名字是對大小寫敏感的。
    如果lpName指定的名字,與一個存在的命名的事件對象的名稱相同,函數將請求EVENT_ALL_ACCESS來訪問存在的對象。這時候,由於bManualReset和bInitialState參數已經在創建事件的進程中設置,這兩個參數將被忽略。如果lpEventAttributes是參數不是NULL,它將確定此句柄是否可以被繼承,但是其安全描述符成員將被忽略。
    如果lpName爲NULL,將創建一個無名的事件對象。
    如果lpName的和一個存在的信號、互斥、等待計時器、作業或者是文件映射對象名稱相同,函數將會失敗,在GetLastError函數中將返ERROR_INVALID_HANDLE。造成這種現象的原因是這些對象共享同一個命名空間;
    【返回值】
  • WAIT_OBJECT_0 :表示等待對象已經變爲有信號狀態,如果設置爲自動復位,還會把此信號再次變爲無信號狀態;
  • WAIT_TIMEOUT :表示等待超時;
  • WAIT_FAILED :表示等待對象句柄是一個無效句柄;

CreateEvent實現多線程

主要測試CreateEvent中bManualReset:和bInitialState參數的取值在線程調用中信號狀態的情況。

Test_one:

bManualReset:TRUE
bInitialState:TRUE

CreateEvent(NULL, TRUE, TRUE, NULL); //使用手動重置爲無信號狀態,初始化時有信號狀態

    #include "iostream"  
    #include "windows.h"  
    using namespace std;  

    DWORD WINAPI ThreadProc1(LPVOID lpParam);  
    DWORD WINAPI ThreadProc2(LPVOID lpParam);  
    HANDLE hEvent = NULL;  
    HANDLE hThread1 = NULL;  
    HANDLE hThread2 = NULL;  
    int main(int argc, char *args[])  
    {  
        hEvent = CreateEvent(NULL, TRUE, TRUE, NULL)</span>; //使用手動重置爲無信號狀態,初始化時有信號狀態  
        //hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); //當一個等待線程被釋放時,自動重置爲無信號狀態,初始是有信號狀態  
        //if (SetEvent(hEvent))  
        //{  
        //  cout << "setEvent 成功" <<endl;  
        //}  
        hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc1, NULL, 0,NULL);  
        Sleep(200);  
        hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc2, NULL, 0,NULL);  
        Sleep(200);  
        if ( NULL == hThread1)  
        {  
            cout <<"create thread fail!";  
        }  
        //DWORD dCount = ResumeThread(hThread);  
        //cout << LOWORD(dCount) << endl;  
        return 0;  
    }  
    DWORD WINAPI ThreadProc1(LPVOID lpParam)  
    {  
        cout <<"in thread1@!"<<endl;  

        DWORD dReturn = WaitForSingleObject(hEvent,INFINITE);  

        if ( WAIT_OBJECT_0 == dReturn)  
        {  
            cout <<" thread1 signaled ! "<<endl;  
        }  
        cout <<"in thread1 --signal"<<endl;  

        //SetEvent(hEvent);  
        return 0;  
    }  
    DWORD WINAPI ThreadProc2(LPVOID lpParam)  
    {  
        cout <<"in thread2@!"<<endl;  

        DWORD dReturn = WaitForSingleObject(hEvent,INFINITE);  

        if ( WAIT_OBJECT_0 == dReturn)  
        {  
            cout <<"thread2 signaled ! "<<endl;  
        }  
        cout <<"in thread2--signal"<<endl;  

        return 0;  
    }  

運行結果:

in thread1@!
thread1 signaled !
in thread1 --signal
in thread2@!
thread2 signaled !
in thread2 --signal

從結果中看,執行完線程1又執行了線程2.
由於hEvent = CreateEvent(NULL, TRUE, TRUE, NULL),使用手動重置爲無信號狀態,初始化時有信號狀態
所以hEvent一直處於有信號狀態,在線程1釋放後,hEvent仍處於有信號狀態,所以線程2正常執行。

Test_two:

bManualReset:FALSE

bInitialState:TRUE

hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); //當一個等待線程被釋放時,自動重置爲無信號狀態,初始是有信號狀態  

運行結果:

in thread1@!
thread1 signaled !
in thread1 --signal
in thread2@!

從執行結果中分析,執行了線程1,線程2一直在等待,直到主線程結束。
由於hEvent = CreateEvent(NULL, FALSE, TRUE, NULL),當一個等待線程被釋放時,自動重置爲無信號狀態,初始是有信號狀態。
初始執行線程1的時候,hEvent是有信號的,所以線程1正常執行;又由於bManualReset=FALSE,所以執行完線程1後,事件自動重置爲無信號狀態,所以線程2一直在等待
WaitForSingleObject(hEvent,INFINITE); 函數一直在等待hEvent變爲有信號狀態,但是當主線程執行完,還沒等待到,線程2程序一直沒有走下去。

Test_three:

bManualReset:TRUE
bInitialState:FALSE

hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//使用手動重置爲無信號狀態,初始化時爲無信號狀態

執行結果是:

in thread1@!
in thread2@!

此時,如果放開例子中的註釋部分:

if (SetEvent(hEvent))   //設置信號爲有信號狀態
{
cout << "setEvent 成功" <<endl;
}

可見,線程1和線程2都執行了。
因爲調用SetEvent,事件變爲有信號狀態,線程1執行;又由於線程1釋放後,hEvent仍舊處於有信號狀態,所以線程2也執行了。

再修改:在線程1中,添加ResetEvent(hEvent)(手動設置事件爲無信號狀態),則線程2不會執行。

Test_four:

bManualReset:FALSE
bInitialState:FALSE

hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//線程釋放後自動重置爲無信號狀態,初始化時爲無信號狀態
    #include "iostream"  
    #include "windows.h"  
    using namespace std;  

    DWORD WINAPI ThreadProc1(LPVOID lpParam);  
    DWORD WINAPI ThreadProc2(LPVOID lpParam);  
    HANDLE hEvent = NULL;  
    HANDLE hThread1 = NULL;  
    HANDLE hThread2 = NULL;  
    int main(int argc, char *args[])  
    {  
        //hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); //使用手動重置爲無信號狀態,初始化時有信號狀態  
        //hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); //當一個等待線程被釋放時,自動重置爲無信號狀態,初始是有信號狀態  
        //hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//使用手動重置爲無信號狀態,初始化時爲無信號狀態  
        hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//使用手動重置爲無信號狀態,初始化時爲無信號狀態  
        if (SetEvent(hEvent))  
        {  
            cout << "setEvent 成功" <<endl;  
        }  
        hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc1, NULL, 0,NULL);  
        Sleep(200);  
        hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc2, NULL, 0,NULL);  
        Sleep(200);  
        if ( NULL == hThread1)  
        {  
            cout <<"create thread fail!";  
        }  
        //DWORD dCount = ResumeThread(hThread);  
        //cout << LOWORD(dCount) << endl;  
        return 0;  
    }  
    DWORD WINAPI ThreadProc1(LPVOID lpParam)  
    {  
        cout <<"in thread1@!"<<endl;  

        DWORD dReturn = WaitForSingleObject(hEvent,INFINITE);  

        if ( WAIT_OBJECT_0 == dReturn)  
        {  
            cout <<" thread1 signaled ! "<<endl;  
        }  
        cout <<"in thread1 --signal"<<endl;  

        //SetEvent(hEvent);  
        return 0;  
    }  
    DWORD WINAPI ThreadProc2(LPVOID lpParam)  
    {  
        cout <<"in thread2@!"<<endl;  

        DWORD dReturn = WaitForSingleObject(hEvent,INFINITE);  

        if ( WAIT_OBJECT_0 == dReturn)  
        {  
            cout <<"thread2 signaled ! "<<endl;  
        }  
        cout <<"in thread2--signal"<<endl;  

        return 0;  
    }  

執行結果:

setEvent成功
in thread1@!
thread1 signaled !
in thread1 --signal
in thread2@!

由於調用SetEvent,hEvent爲有信號狀態,線程1正常執行,又由於調用完線程1後,hEvent自動重置爲無信號狀態,所以線程2只能在等待,直到主線程退出。

修改:線程1中的SetEvent(hEvent);的註釋去掉,再運行,則線程1和線程2 都會執行

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章