windows多線程同步--事件


推薦參考博客:秒殺多線程第六篇 經典線程同步 事件Event

 

事件是內核對象,多用於線程間通信,可以跨進程同步

事件主要用到三個函數:CreateEvent,OpenEvent,SetEvent,ResetEvent                                                        本文地址

 

CreateEvent

函數功能:創建事件

函數原型:

HANDLECreateEvent(

LPSECURITY_ATTRIBUTESlpEventAttributes,

BOOLbManualReset,

BOOLbInitialState,

LPCTSTRlpName

);

第一個參數:表示安全控制,一般直接傳入NULL。

第二個參數:確定事件是手動置位還是自動置位,傳入TRUE表示手動置位,傳入FALSE表示自動置位。如果為自動置位,則對該事件調用WaitForSingleObject()后會自動調用ResetEvent()使事件變成未觸發狀態。打個小小比方,手動置位事件相當於教室門,教室門一旦打開(被觸發),所以有人都可以進入直到老師去關上教室門(事件變成未觸發)。自動置位事件就相當於醫院里拍X光的房間門,門打開后只能進入一個人,這個人進去后會將門關上,其它人不能進入除非門重新被打開(事件重新被觸發)。

第三個參數:表示事件的初始狀態,傳入TRUR表示已觸發。

第四個參數:表示事件的名稱,傳入NULL表示匿名事件。

返回值:事件的句柄

 

OpenEvent

函數功能:根據名稱獲得一個事件句柄。

函數原型:

HANDLEOpenEvent(

DWORDdwDesiredAccess,

BOOLbInheritHandle,

LPCTSTRlpName     //名稱

);

函數說明:

第一個參數:表示訪問權限,對事件一般傳入EVENT_ALL_ACCESS。詳細解釋可以查看MSDN文檔。

第二個參數:表示事件句柄繼承性,一般傳入TRUE即可。

第三個參數:表示名稱,不同進程中的各線程可以通過名稱來確保它們訪問同一個事件。

返回值:返回事件的句柄

 

SetEvent

函數功能:觸發事件

函數原型:BOOLSetEvent(HANDLEhEvent);

函數說明:每次觸發后,必有一個或多個處於等待狀態下的線程變成可調度狀態。

 

ResetEvent

函數功能:將事件設為末觸發

函數原型:BOOLResetEvent(HANDLEhEvent);

 

下面從一個例子說明:假設有三個線程都需要使用打印機,我們可以使用互斥量來控制,這樣就可以保證每次只有一個線程在使用打印機

使用自動置位,那么在調用WaitForSingleObject()后會自動調用ResetEvent()使事件變為未觸發狀態,為了使后面的線程能夠繼續打印,需要在線程函數的結尾調用SetEvent來觸發事件

 #include<string>
 #include<iostream>
 #include<process.h>
 #include<windows.h>
 using namespace std;

 //聲明事件句柄
 HANDLE hev;

//線程綁定的函數返回值和參數是確定的,而且一定要__stdcall
unsigned __stdcall threadFun(void *param)
{
    WaitForSingleObject(hev, INFINITE);//等待事件被觸發
    for(int i = 0; i < 10; i++)
        cout<<*(string *)(param)<<" ";
    cout<<endl;
    SetEvent(hev);//設置事件為觸發狀態,使后面的線程可以打印
    return 1;
}


int main()
{
    //創建一個未被觸發的事件,事件是自動置位的
    hev = CreateEvent(NULL, FALSE, FALSE, NULL);

    SetEvent(hev);// 觸發事件,使線程可以打印

    HANDLE hth1, hth2, hth3;
    string s1 = "first", s2 = "second", s3 = "third";

    //創建線程
    hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
    hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
    hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);

    //等待子線程結束
    WaitForSingleObject(hth1, INFINITE);
    WaitForSingleObject(hth2, INFINITE);
    WaitForSingleObject(hth3, INFINITE);

    //一定要記得關閉線程句柄
    CloseHandle(hth1);
    CloseHandle(hth2);
    CloseHandle(hth3);

    //千萬別忘了刪除事件
    CloseHandle(hev);
}

 

image

 

使用手動置位,調用WaitForSingleObject()后事件就一直是觸發狀態,線程可以任意的打印

 #include<string>
 #include<iostream>
 #include<process.h>
 #include<windows.h>
 using namespace std;

 //聲明事件句柄
 HANDLE hev;

//線程綁定的函數返回值和參數是確定的,而且一定要__stdcall
unsigned __stdcall threadFun(void *param)
{
    WaitForSingleObject(hev, INFINITE);//等待事件被觸發
    for(int i = 0; i < 10; i++)
        cout<<*(string *)(param)<<" ";
    cout<<endl;
    //SetEvent(hev);//設置事件為觸發狀態,使后面的線程可以打印
    return 1;
}


int main()
{
    //創建一個未被觸發的事件,事件是手動置位的
    hev = CreateEvent(NULL, TRUE, FALSE, NULL);

    SetEvent(hev);// 觸發事件,使線程可以打印

    HANDLE hth1, hth2, hth3;
    string s1 = "first", s2 = "second", s3 = "third";

    //創建線程
    hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
    hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
    hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);

    //等待子線程結束
    WaitForSingleObject(hth1, INFINITE);
    WaitForSingleObject(hth2, INFINITE);
    WaitForSingleObject(hth3, INFINITE);

    //一定要記得關閉線程句柄
    CloseHandle(hth1);
    CloseHandle(hth2);
    CloseHandle(hth3);

    //千萬別忘了刪除事件
    CloseHandle(hev);
}

 

image

 

再通過下面的例子來看看時間有沒有所有權屬性:編寫一個程序,開啟3個線程,這3個線程的ID分別為A、B、C,每個線程將自己的ID在屏幕上打印10遍,要求輸出結果必須按ABC的順序顯示;如:ABCABC….依次遞推

 #include<string>
 #include<iostream>
 #include<process.h>
 #include<windows.h>
 using namespace std;
 //聲明3個事件句柄
HANDLE  hev1, hev2, hev3;

//線程綁定的函數返回值和參數是確定的,而且一定要__stdcall
unsigned __stdcall threadFunA(void *)
{
    for(int i = 0; i < 10; i++){
        WaitForSingleObject(hev1, INFINITE);//等待事件1
        cout<<"A";
        SetEvent(hev2);//觸發事件2
    }
    return 1;
}
unsigned __stdcall threadFunB(void *)
{
    for(int i = 0; i < 10; i++){
        WaitForSingleObject(hev2, INFINITE);//等待事件2
        cout<<"B";
        SetEvent(hev3);//觸發事件3
    }
    return 2;
}
unsigned __stdcall threadFunC(void *)
{
    for(int i = 0; i < 10; i++){
        WaitForSingleObject(hev3, INFINITE);//等待事件3
        cout<<"C";
        SetEvent(hev1);//觸發事件1
    }
    return 3;
}


int main()
{
    hev1 = CreateEvent(NULL, FALSE, FALSE, NULL);
    hev2 = CreateEvent(NULL, FALSE, FALSE, NULL);
    hev3 = CreateEvent(NULL, FALSE, FALSE, NULL);
    SetEvent(hev1);//觸發事件1,從A開始打印

    HANDLE hth1, hth2, hth3;
    //創建線程
    hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL);
    hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL);
    hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFunC, NULL, 0, NULL);

    //等待子線程結束
    WaitForSingleObject(hth1, INFINITE);
    WaitForSingleObject(hth2, INFINITE);
    WaitForSingleObject(hth3, INFINITE);

    //一定要記得關閉線程句柄
    CloseHandle(hth1);
    CloseHandle(hth2);
    CloseHandle(hth3);

    //刪除事件
    CloseHandle(hev1);
    CloseHandle(hev2);
    CloseHandle(hev3);
}

 

image

 

由結果可知事件不具有所有權屬性,即某個線程獲取事件后,一定要等待事件再次被觸發。可參考本博客其他文章中臨界區、互斥量、信號量的所有權屬性來理解。

 

【版權聲明】轉載請注明出處http://www.cnblogs.com/TenosDoIt/p/3601458.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM