CreateEvent的使用方法


 CreateEvent的使用方法收藏

新一篇: PreCreateWindow的作用和用法 | 舊一篇: VC中_T()的作用

事件對象就像一個開關:它僅僅有兩種狀態---開和關。當一個事件處於”開”狀態,我們稱其為”有信號”否則稱為”無信號”。能夠在一個線程的運行函數中創建一個事件對象,然后觀察它的狀態,假設是”無信號”就讓該線程睡眠,這樣該線程占用的CPU時間就比較少。

產生事件對象的函數例如以下:  

HANDLE     CreateEvent(

        LPSECURITY_ATTRIBUTES     lpEventAttributes,     //     SD   
        BOOL     bManualReset,                                                 //     reset     type   
        BOOL     bInitialState,                                                      //     initial     state   
        LPCTSTR     lpName                                                       //     object     name   
    );   
    該函數創建一個Event同步對象,假設CreateEvent調用成功的話,會返回新生成的對象的句柄,否則返回NULL。

參數說明:
    lpEventAttributes     一般為NULL   


    bManualReset               創建的Event是自己主動復位還是人工復位.假設true,人工復位,   一旦該Event被設置為有信號,則它一直會等到ResetEvent()API被調用時才會恢復 為無信號.     假設為false,Event被設置為有信號,則當有一個wait到它的Thread時,  該Event就會自己主動復位,變成無信號.   假設想 在每次調用WaitForSingleObject 后讓WINDOWS為您自己主動地把事件地狀態恢復為”無信號”狀態,必須把該參數設為FALSE,否則,您必須每次調用ResetEvent函數來清除事件 的信號。


    bInitialState             初始狀態,true,有信號,false無信號   
    lpName                  事件對象的名稱。您在OpenEvent函數中可能使用。

凝視:
    一個Event被創建以后,能夠用OpenEvent()API來獲得它的Handle,用CloseHandle()    來關閉它,用SetEvent()或PulseEvent()來設置它使其有信號,用ResetEvent()       來使其無信號,用WaitForSingleObject()或WaitForMultipleObjects()來等待其變為有信號.   
   
    PulseEvent()是一個比較有意思的用法,正如這個API的名字,它使一個Event 對象的狀態發生一次脈沖變化,從無信號變成有信號再變成無信號,而整個操作是原子的.   
    對自己主動復位的Event對象,它僅釋放第一個等到該事件的thread(假設有),而對於人工復位的Event對象,它釋放全部等待的thread.  
 

 
這里有兩個API函數用來改動事件對象的信號狀態:SetEvent和ResetEvent。前者把事件對象設為”有信號”狀態,而后者正好相反。
在事件對象生成后,必須調用WaitForSingleObject來讓線程進入等待狀態,該函數的語法例如以下:  

WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD  

hObject -->指向同步對象的指針。事件對象事實上是同步對象的一種。
dwTimeout --> 等待同步對象變成”有信號”前等待的時間,以毫秒計。當等待的時間超過該值后無信號同步對象仍處於”無信號”狀態,線程不再等待, WaitForSingleObject函數會返回。假設想要線程一直等待,請把該參數設為INFINITE(該值等於0xffffffff)。  

發表於 @ 2008年04月18日 13:30:00|評論(0)|編輯

新一篇: PreCreateWindow的作用和用法 | 舊一篇: VC中_T()的作用



 Visual C++中的多線程收藏

新一篇: 特洛伊木馬(一) | 舊一篇: 用戶接口與作業調度

以前,以前研究過了java中的多線程問題,特別是加鎖和同步問題,可是,在C++中,確沒有這么簡單了。因為C沒有提供像java里的線程類,一些同步的實現必須靠自己程序實現,稍顯復雜。
一般來說,在C++里面創建和終止線程的函數為:_beginthread和_endthread兩個函數,當然,也能夠用CreateThread和ExitThread。詳細的使用方式能夠查看msdn。
那么,怎么樣實現加鎖與同步呢?能夠使用createMutex函數以及createEvent方法等來實現,詳細能夠參考下例:

#include 
<  iostream >
#include 
<  windows.h >

using   namespace  std;
#define  BUFSIZE 5
int  SharedBuffer[BUFSIZE];
int  head,tail;
int  count;
HANDLE hMutex;
HANDLE hNotFullEvent, hNotEmptyEvent;
void  BB_Producer()
{
int i;
for (i=20; i>=0; i--{
while(1{
WaitForSingleObject(hMutex,INFINITE);
if (count == BUFSIZE) // 緩沖區滿
ReleaseMutex(hMutex);
// 等待直到緩沖區非滿
WaitForSingleObject(hNotFullEvent,INFINITE);
continue;
}

// 得到相互排斥鎖且緩沖區非滿,跳出while循環
break;
}

// 得到相互排斥鎖且緩沖區非滿,開始產生新數據
cout << "Produce: " << i << endl;
SharedBuffer[tail] 
= i;
tail 
= (tail+1% BUFSIZE;
count
++;
ReleaseMutex(hMutex); 
// 結束臨界區
PulseEvent(hNotEmptyEvent); // 喚醒消費者線程
}

}

void  BB_Consumer()
{
int result;
while (1{
WaitForSingleObject(hMutex,INFINITE);
if (count == 0// 沒有能夠處理的數據
ReleaseMutex(hMutex); // 釋放相互排斥鎖且等待
// 等待直到緩沖區非空
WaitForSingleObject(hNotEmptyEvent,INFINITE);
}

else if (SharedBuffer[head] == 0{
cout 
<< "Consumed 0: end of data" << endl;
ReleaseMutex(hMutex); 
// 結束臨界區
ExitThread(0);
}

else // 獲得相互排斥鎖且緩沖區有數據,開始處理
result = SharedBuffer[head];
cout 
<< "Consumed: " << result << endl;
head 
= (head+1% BUFSIZE;
count
--;
ReleaseMutex(hMutex); 
// 結束臨界區
PulseEvent(hNotFullEvent); // 喚醒生產者線程
}

}

}

void  main()
{
HANDLE hThreadVector[
2];
DWORD ThreadID;
count 
= 0;
head 
= 0;
tail 
= 0;
hMutex 
= CreateMutex(NULL,FALSE,NULL);
hNotFullEvent 
= CreateEvent(NULL,TRUE,FALSE,NULL);
hNotEmptyEvent 
= CreateEvent(NULL,TRUE,FALSE,NULL);
hThreadVector[
0= CreateThread (NULL, 0,
(LPTHREAD_START_ROUTINE) BB_Producer,
NULL, 
0, (LPDWORD)&ThreadID);
hThreadVector[
1= CreateThread (NULL, 0,
(LPTHREAD_START_ROUTINE) BB_Consumer,
NULL, 
0, (LPDWORD)&ThreadID);
WaitForMultipleObjects(
2,hThreadVector,TRUE,INFINITE);
}
這是一個典型的生產者-消費者問題,它們公用的資源是SharedBuffer,當Buffer中有數據且未滿時,兩個線程都能夠執行,當Buffer為 空時,Consumer就要等待,直到Buffer不為空,這里就是用event來實現的;相同,當Buffer為滿時,Producer就要等待。

發表於 @ 2006年10月29日 19:54:00|評論(2)|編輯

新一篇: 特洛伊木馬(一) | 舊一篇: 用戶接口與作業調度


Windows API一日一練(45)CreateEvent和SetEvent函數收藏

新一篇: Windows API一日一練(46)EnterCriticalSection和LeaveCriticalSection函數 | 舊一篇: Windows API一日一練(44)wsprintf函數

當你創建一個線程時,事實上那個線程是一個循環,不像上面那樣僅僅執行一次的。這樣就帶來了一個問題,在那個死循環里要找到合適的條件退出那個死循環,那么是怎么樣實現它的呢?在 Windows 里往往是採用事件的方式,當然還能夠採用其他的方式。在這里先介紹採用事件的方式來通知從線程執行函數退出來,它的實現原理是這樣,在那個死循環里不斷地使用 WaitForSingleObject 函數來檢查事件是否滿足,假設滿足就退出線程,不滿足就繼續執行。當在線程里執行堵塞的函數時,就須要在退出線程時,先要把堵塞狀態變成非堵塞狀態,比方使用一個線程去接收網絡數據,同一時候使用堵塞的 SOCKET 時,那么要先關閉 SOCKET ,再發送事件信號,才干夠退出線程的。以下就來演示怎么樣使用事件來通知線程退出來。
 
函數 CreateEvent 聲明例如以下:
 
WINBASEAPI
__out
HANDLE
WINAPI
CreateEventA(
    __in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
    __in     BOOL bManualReset,
    __in     BOOL bInitialState,
    __in_opt LPCSTR lpName
    );
WINBASEAPI
__out
HANDLE
WINAPI
CreateEventW(
    __in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
    __in     BOOL bManualReset,
    __in     BOOL bInitialState,
    __in_opt LPCWSTR lpName
    );
#ifdef UNICODE
#define CreateEvent CreateEventW
#else
#define CreateEvent CreateEventA
#endif // !UNICODE
lpEventAttributes 是事件的屬性。
bManualReset 是指事件手動復位,還是自己主動復位狀態。
bInitialState 是初始化的狀態是否處於有信號的狀態。
lpName 是事件的名稱,假設有名稱,能夠跨進程共享事件狀態。
 
調用這個函數的樣例例如以下:
#001 #pragma once
#002 
#003 // 線程類。
#004 // 蔡軍生 2007/09/23 QQ:9073204
#005 class CThread
#006 {
#007 public:
#008 
#009  CThread(void)
#010  {
#011          m_hThread = NULL;
#012          m_hEventExit = NULL;
#013  }
#014 
#015  virtual ~CThread(void)
#016  {
#017         if (m_hThread)
#018         {
#019               // 刪除的線程資源。
#020               ::CloseHandle(m_hThread);
#021         }
#022 
#023         if (m_hEventExit)
#024         {
#025               // 刪除事件。
#026               ::CloseHandle(m_hEventExit);
#027         }
#028        
#029  }
#030 
#031  // 創建線程
#032  HANDLE CreateThread(void)
#033  {
#034         // 創建退出事件。
#035         m_hEventExit = ::CreateEvent(NULL,TRUE,FALSE,NULL);
#036         if (!m_hEventExit)
#037          {
#038               // 創建事件失敗。
#039               return NULL;
#040         }
#041 
#042         // 創建線程。
#043          m_hThread = ::CreateThread(
#044               NULL,                    // 安全屬性使用缺省。
#045               0,                         // 線程的堆棧大小。
#046               ThreadProc,                 // 線程執行函數地址。
#047               this,                      // 傳給線程函數的參數。
#048               0,                         // 創建標志。
#049               &m_dwThreadID);        // 成功創建后的線程標識碼。
#050 
#051         return m_hThread;
#052  }
#053 
#054  // 等待線程結束。
#055  void WaitFor(DWORD dwMilliseconds = INFINITE)
#056  {
#057         // 發送退出線程信號。
#058        ::SetEvent(m_hEventExit);
#059 
#060         // 等待線程結束。
#061         ::WaitForSingleObject(m_hThread,dwMilliseconds);
#062  }
#063 
#064 protected:
#065  //
#066  // 線程執行函數。
#067  // 蔡軍生 2007/09/21
#068  //
#069  static DWORD WINAPI ThreadProc(LPVOID lpParameter)
#070  {
#071         // 轉換傳送入來的參數。
#072         CThread* pThread = reinterpret_cast<CThread *>(lpParameter);
#073         if (pThread)
#074         {
#075               // 線程返回碼。
#076               // 調用類的線程處理函數。
#077               return pThread->Run();
#078         }
#079        
#080         //
#081         return -1;       
#082  }
#083 
#084  // 線程執行函數。
#085  // 在這里能夠使用類里的成員,也能夠讓派生類實現更強大的功能。
#086  // 蔡軍生 2007/09/25
#087  virtual DWORD Run(void)
#088  {
#089         // 輸出到調試窗體。
#090         ::OutputDebugString(_T("Run() 線程函數執行 /r/n"));     
#091 
#092         // 線程循環。
#093         for (;;)
#094         {
#095              DWORD dwRet = WaitForSingleObject(m_hEventExit,0);
#096               if (dwRet == WAIT_TIMEOUT)
#097               {
#098                    // 能夠繼續執行。                 
#099                    TCHAR chTemp[128];
#100                    wsprintf(chTemp,_T("ThreadID=%d/r/n"),m_dwThreadID);
#101                    ::OutputDebugString(chTemp);
#102 
#103                     // 眼下沒有做什么事情,就讓線程釋放一下 CPU
#104                    Sleep(10);
#105               }
#106               else if (dwRet == WAIT_OBJECT_0)
#107               {
#108                    // 退出線程。
#109                    ::OutputDebugString(_T("Run() 退出線程 /r/n"));
#110                    break;
#111               }
#112               else if (dwRet == WAIT_ABANDONED)
#113               {
#114                    // 出錯。
#115                    ::OutputDebugString(_T("Run() 線程出錯 /r/n"));
#116                    return -1;
#117               }
#118         }
#119 
#120         return 0;
#121  }
#122 
#123 protected:
#124  HANDLE m_hThread;         // 線程句柄。
#125  DWORD m_dwThreadID;          // 線程 ID
#126 
#127  HANDLE m_hEventExit;    // 線程退出事件。
#128 };
#129 
 
上面在第 35 行創建線程退出事件,第 95 行檢查事件是否可退出線程執行,第 58 行設置退出線程的事件。
 

發表於 @ 2007年09月25日 21:32:00|評論(0)|編輯

新一篇: Windows API一日一練(46)EnterCriticalSection和LeaveCriticalSection函數 | 舊一篇: Windows API一日一練(44)wsprintf函數


Win32 API 經常使用函數之二收藏

新一篇: Win32 API 經常使用函數之三——注冊表操作(上) | 舊一篇: Win32 API 經常使用函數之中的一個

【事件】
       事件用處多是控制線程間的同步。
       最典型的應用就是CreateThread之后等待線程函數的啟動。如Main線程里CreateThread,它之后的操作依賴於子線程,那么它通常會 在CreateThread之后推斷HANDLE是否有效,然后進入等待。(當然在這之前,一個Event是已經創建好的,並初始化為未通知狀態)子線程 啟動后完畢了初始化操作,並設置Event為已通知狀態。這時,一直在等待該事件的Main線程發現該事件已經得到通知,因此它就變成可調度線程。這時 Main線程知道子線程已經完畢了初始化操作。
       CreateEvent函數用於創建一個Event,其原型例如以下:
HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes, 
  BOOL bManualReset, 
  BOOL bInitialState, 
  LPTSTR lpName 
); 
       參數說明:
  1. 第一個參數同CreateThread相似,也是安全級別相關,通常被被設置為NULL,以獲得默認的安全級別。
  2. 第二個參數是個布爾值,它可以告訴系統是創建一個人工重置的事件(TRUE)還是創建一個自己主動重置的事件( FALSE)。
  3. 第三個參數也是布爾值,用於指明該事件是要初始化為已通知狀態(TRUE)還是未通知狀態(FALSE)。
  4. 第四個參數是一個字符串,用於標示這個事件的名字。
       下面是具體說明:
  1. 已通知狀態和未通知狀態
    事件僅僅有兩種狀態,已通知表示這個事件已經被設置過了(能夠理解為發生了),未通知表示還沒有發生。一般設置為未通知狀態,並由SetEvent設置為已 通知狀態。當然也能夠反着做,CreateEvent時設置為已通知狀態,然后由ResetEvent設置為未通知狀態。
  2. 人工重置與自己主動重置
    自己主動重置的事件定義了應該成功等待的副作用規則,即當線程成功地等待到該對象時,自己主動重置的事件就會自己主動重置到未通知狀態。
    人工重置則須要調用ResetEvent函數設置為未通知狀態。
  3. 名字共享
    這個參數非常重要,Win32 API中有非常多方法有這個參數,它遵從一種按名字共享的規則。
    假設傳入一個非NULL字符串(最多260個字符),那么在全局空間,共享該HANDLE,這個全局可以是跨進程的名字空間,即在還有一個進程中依舊可以使用該名字的HANDLE。
    假設希望避免這樣的全局范圍內的共享,那么應該傳入NULL,以一種匿名的方式創建Event等,這樣,它僅僅在當前線程內可見。
        當進程A創建了一個Event后,如CreateEvent(NULL,FALSE,FLASE,_T(“UniqueEvent”));進程B相同創建 了一個Event,也想起名字為UniqueEvent,那么就會出現故障:CreateEvent(NULL,FALSE,FALSE,_T (“UniqueEvent”));系統會首先查看是否已經存在了一個名字為“UniqueEvent”的對象,因為確實存在了一個帶有改名字的內核對 象,因此內核要檢查對象類型,相同是一 個Event,那么系統會運行一次安全檢查,以確定調用者是否擁有對該對象的完整訪問權。假設有這樣的訪問權,系統會在進程B的句柄表里找到一個空項目,對 其初始化,使得該項指向現有的內核對象。假設類型不匹配,或者拒絕訪問,那么進程B的CreateEvent會失敗。
       應用程序可以確定它是否確實創建了一個新內核對象,而不是打開了一個現有的對象。方法是在調用C r e a t e *函數后馬上調用G e t L a s t E r r o r:假設為ERROR_ALREADY_EXISTS,那么表示系統內已經存在了這樣名字的對象。
       Open*是去查看名字空間中是否有這個名字的內核對象存在調用C r e a t e *函數與調用O p e n *函數之間的主要區別是,假設對象並不存在,那么C r e a t e *函數將創建該對象,而O p e n *函數則執行失敗。

       PulseEvent函數使得事件變為已通知狀態,然后馬上又變為未通知狀態,這就像在調用SetEvent后又馬上調用ResetEvent函數一樣。 假設在人工重置的事件上調用PulseEvent函數,那么在發出該事件時,等待該事件的不論什么一個線程或全部線程將變為可調度線程。假設在自己主動重置事件上 調用P u l s e E v e n t函數,那么僅僅有一個等待該事件的線程變為可調度線程。假設在發出事件時沒有不論什么線程在等待該事件,那么將不起不論什么作用。

【等待函數】
       等待函數用來監聽事件的已通知狀態。WaitForSingleObject和WaitForMultipleObjects兩個函數分別用以等待單個事件和多個事件。
DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD dwMilliseconds
);
DWORD WaitForMultipleObjects(
  DWORD nCount,
  
const  HANDLE *  lpHandles,
  BOOL bWaitAll,
  DWORD dwMilliseconds
);
       從函數原型上來看可知,事件的含義是可以支持被通知/未通知的內核對象(比如進程和線程,當傳入的是進程或者線程句柄時,他表示等該線程或進程被標識為終止執行為止。)。
       dwMilliseconds 參數表明等待的時間,假設在這個時間段中事件為已通知狀態,那么對於Single版本號將返回WAIT_OBJECT_0,對於Multiple版本號將返回 WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1)。假設沒有等到將返回WAIT_TIMEOUT。
       Multiple版本號中的bWaitAll表示想要讓它使用何種方式等待。假設為該參數傳遞TRUE,那么在全部對象變為已通知狀態之前,該函數將不同意調用線程執行。通常是FALSE,即僅僅要有一個事件被對應,則線程可調度。

      

發表於 @ 2007年10月28日 18:04:00|評論(0)|編輯

新一篇: Win32 API 經常使用函數之三——注冊表操作(上) | 舊一篇: Win32 API 經常使用函數之中的一個


免責聲明!

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



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