CreateEvent的用法
事件對象就像一個開關:它只有兩種狀態---開和關。當一個事件處于”開”狀態,我們稱其為”有信號”否則稱為”無信號”。可以在一個線程的執行函數中創建一個事件對象,然后觀察它的狀態,如果是”無信號”就讓該線程睡眠,這樣該線程占用的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?
);? ??? ?? 參數說明:
事件只有兩種狀態,已通知表示這個事件已經被設置過了(可以理解為發生了),未通知表示還沒有發生。一般設置為未通知狀態,并由SetEvent設置為已 通知狀態。當然也可以反著做,CreateEvent時設置為已通知狀態,然后由ResetEvent設置為未通知狀態。
自動重置的事件定義了應該成功等待的副作用規則,即當線程成功地等待到該對象時,自動重置的事件就會自動重置到未通知狀態。
人工重置則需要調用ResetEvent函數設置為未通知狀態。
這個參數很重要,Win32 API中有很多方法有這個參數,它遵從一種按名字共享的規則。
如果傳入一個非NULL字符串(最多260個字符),那么在全局空間,共享該HANDLE,這個全局可以是跨進程的名字空間,即在另一個進程中依然能夠使用該名字的HANDLE。
如果希望避免這種全局范圍內的共享,那么應該傳入NULL,以一種匿名的方式創建Event等,這樣,它只在當前線程內可見。
??? ?? 應用程序能夠確定它是否確實創建了一個新內核對象,而不是打開了一個現有的對象。方法是在調用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)|編輯
總結
以上是生活随笔為你收集整理的CreateEvent的用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java获取http状态码_java获取
- 下一篇: CreateThread