您好,登錄后才能下訂單哦!
事件(Event)同步對象
(內核級別)事件內核對象包含:
1 一個使用計數器
2 一個表示事件是否是自動重置還是手動重置的布爾值
3 一個表示事件有沒有被觸發的布爾值
4 當觸發為true時,等待該事件的線程變為可調度狀態
5 事件的觸發表示一個操作已經完成
作用: 通知其他線程,我已經完成讀寫操作了,輪到你們來做了。
他分為兩種類型:
1是手動重置事件,也就是要進行手動的觸發和非觸發狀態的切換.
2是自動重置事件,這種情況下只需要設置觸發事件,不用管什么時候切換觸發狀態。
盡量使用手動重置方式, 因為這種方式可控性強,不易出錯.自動重置事件會引起成功等待的一些副作用.
相關的api:
1 CreateEvent函數
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,//安全屬性
BOOL bManualReset, //復位方式
BOOL bInitialState,//初始狀態
LPCTSTR lpName //對象名稱);
返回一個Handle,事件同步對象的句柄。
參數1 lpEventAttributes 權限,一般NULL就是默認權限
參數2 bManualReset TRUE代表手動重置,FALSE自動重置
參數3 bInitialState TRUE代表可觸發, FALSE非觸發(阻塞)
參數3 lpName 一個對象的名稱,跨進程尋址,一般NULL
2 SetEvent函數,設置事件對象為有信號狀態
BOOL SetEvent( HANDLE hEvent);
hEvent 設置事件對象的句柄 就是CreateEvent返回的句柄.
當調用這個函數后,這個事件就是觸發的狀態。
3 ResetEvent 函數,設置事件對象為無信號,非觸發
BOOL ResetEvent(HANDLE hEvent);
hEvent 設置事件對象的句柄 就是CreateEvent返回的句柄.
當調用這個函數后,這個事件就是非觸發的狀態。
使用例子
還是用之前的代碼,就是一個小球碰到邊界會反彈的程序.
我們需要三個線程,三個全局的事件對象句柄.
WndProc3中代碼如下:
case WM_CREATE: { //系統中基于對話框字體的高度 int cyChar = HIWORD(GetDialogBaseUnits()); thrParams3.hwnd = hWnd; thrParams3.cyChar = cyChar; //創建事件對象 g_hEvent1 = CreateEvent(NULL, TRUE,TRUE,NULL); //手動復位 事件 有信號 g_hEvent2 = CreateEvent(NULL, TRUE, FALSE, NULL);//手動復位 事件 無信號 g_hEvent3 = CreateEvent(NULL, TRUE, FALSE, NULL);//手動復位 事件 無信號 // 創建三個線程 HANDLE handleBall1 = CreateThread(NULL, 0, ThrBallProc1, &thrParams3, 0, NULL); HANDLE handleBall2 = CreateThread(NULL, 0, ThrBallProc2, &thrParams3, 0, NULL); HANDLE handleBall3 = CreateThread(NULL, 0, ThrBallProc3, &thrParams3, 0, NULL); //關閉線程句柄 CloseHandle(handleBall1); CloseHandle(handleBall2); CloseHandle(handleBall3); }
上面三個事件都是要手動復位的。
來看線程函數
前面依然使用WaitForSingleObject來進行等待,但是用的是事件的對象, 然后最好要手動的設置事件的信號。
DWORD WINAPI ThrBallProc1(LPVOID lp) { PPARAMS param3 = static_cast<PPARAMS>(lp); //休眠1秒 Sleep(1000); //等待事件 使用INFINITE所以是無限等待 只有觸發才返回 WaitForSingleObject(g_hEvent1, INFINITE); //獲取dc HDC hdc = GetDC(param3->hwnd); //生成隨機數種子 srand(GetTickCount()); //創建筆和畫刷 HPEN white_pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); HBRUSH green_brush = CreateSolidBrush(RGB(0,255,0)); //綠色的小球 HBRUSH white_brush = CreateSolidBrush(RGB(255, 255, 255)); //小球的開始位置 int ball_x = param3->cxClient / 2; int ball_y = param3->cyClient / 2; //速度 int xv = -4 + rand() % 8; int yv = -4 + rand() % 8; DWORD dwCurTime = GetTickCount(); while (1) { //首先選擇白色筆和白色畫刷 設置上下文中去 SelectObject(hdc, white_pen); SelectObject(hdc, white_brush); // 繪制圓形小球 Ellipse(hdc, ball_x, ball_y, ball_x + 32, ball_y + 32); //移動小球 ball_x += xv; ball_y += yv; //如果x軸 碰到邊界 那就往反方向走 if (ball_x <0 || ball_x > param3->cxClient - 32) { xv = -xv; ball_x += xv; } else // 或者是Y軸 { if (ball_y <17 || ball_y > param3->cyClient - 32) { yv = -yv; ball_y += yv; } } SelectObject(hdc, white_pen); SelectObject(hdc, green_brush); //畫小球 Ellipse(hdc, ball_x, ball_y, ball_x + 32, ball_y + 32); DWORD dwTime = GetTickCount() - dwCurTime; //當前時間 和第一次運行的時間差 Sleep(10); //判斷現在的時間 減去初始化時間(循環外的那個時間) 是不是大于10秒鐘 if ((GetTickCount() - dwCurTime) > 1000 * 10) { //完成了當前線程操作 break; } } //線程的工作完成 //刪除GDI對象 DeleteObject(white_brush); DeleteObject(green_brush); DeleteObject(white_pen); ReleaseDC(param3->hwnd,hdc ); //設置窗口無效,并更新窗口 InvalidateRect(param3->hwnd,NULL,TRUE); UpdateWindow(param3->hwnd); // 手動設置事件信號 //讓1號 事件對象 無信號 讓2號事件 有信號 ResetEvent(g_hEvent1); SetEvent(g_hEvent2); return 0; }
我們看到,三個線程都有一個繪制小球,但是他門沒有同步出現,
而是第一個小球操作完10秒(或者是某個操作完成),他必須是有
一個對象,手動的設置觸發,那這個等待函數對應的就會返回。
如果使用自動重置事件呢?
//創建事件對象 g_hEvent1 = CreateEvent(NULL, FALSE,TRUE,NULL); //改成自動復位 g_hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); g_hEvent3 = CreateEvent(NULL, FALSE, FALSE, NULL);
然后在這個等待函數當作
WaitForSingleObject,當等待到了一個Event事件,他是觸發狀態,
他會判斷他是不是自動重置事件的,如果是自動重置事件的話,
他會立即調用ResetEvent將這個事件設置成,非觸發狀態.
這種情況下, 最后可以不掉用這個函數。
因為他在等待函數中,以及調用了這個函數.
然后將這個ResetEvent注釋掉,
這樣就變成自動復位的了。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。