1. 程式人生 > >VC++執行緒同步-事件物件

VC++執行緒同步-事件物件

這是整理孫鑫VC得到的關於執行緒同步方面的筆記.

n事件物件也屬於核心物件,包含一個使用計數,一個用於指明該事件是一個自動重置的事件還是一個人工重置的事件的布林值,另一個用於指明該事件處於已通知狀態還是未通知狀態的布林值。

n有兩種不同型別的事件物件。一種是人工重置的事件,另一種是自動重置的事件。當人工重置的事件得到通知時,等待該事件的所有執行緒均變為可排程執行緒。當一個自動重置的事件得到通知時,等待該事件的執行緒中只有一個執行緒變為可排程執行緒。得到事件物件後,因為是自動重置的事件物件,所以作業系統將該事件物件設定為非訊號狀態

//---------------------------------------------------以下手動重置事件物件不具有實際參考價值----------------------------------------------------------

#include <windows.h>

#include <iostream.h>

DWORD WINAPI Fun1Proc(

LPVOID lpParameter// thread data

);

DWORD WINAPI Fun2Proc(

LPVOID lpParameter// thread data

);

int tickets=100;

HANDLE g_hEvent;  //全域性的控制代碼變數,用來儲存所建立的事件物件的控制代碼。由於多個執行緒要訪問g_hEvent這個物件,所以將其設定為全域性變數。

void main()

{

HANDLE hThread1;

HANDLE hThread2;

hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(hThread1);

CloseHandle(hThread2);

g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);   //第三個狀態為FALSE說明建立時事件初始化為無訊號狀態

/*(1)第二個引數設定TRUE為人工重置事件物件,當人工重置的事件得到通知時,等待該事件的所有執行緒均變為可排程執行緒。也就是說他們都可以同時執行。兩個執行緒可以同時執行說明同步失敗。ResetEvent(g_hEvent)手動

把事件物件設定成非訊號狀態,人工重置必須顯式的SetEvent設定有訊號狀態.(2)第二個引數設定FALSE為自動重置事件物件,當一個自動重置的事件得到通知時,等待該事件的執行緒中只有一個執行緒變為可排程執行緒,同時將該事件物件設定為非訊號狀態。儘量不要用人工重置事件物件,因為會出現下面的問題

*/

SetEvent(g_hEvent);//把事件物件設定成有訊號狀態,但這時候所有的執行緒都變成有訊號狀態,所以每個執行緒前面需要用ResetEvent

Sleep(4000);

CloseHandle(g_hEvent);

}

DWORD WINAPI Fun1Proc(

LPVOID lpParameter// thread data

)

{

while(TRUE)

{

WaitForSingleObject(g_hEvent,INFINITE);

//ResetEvent(g_hEvent);  // 人工重置的事件物件,除非顯示的呼叫ResetEvent(g_hEvent);  將事件物件設定為無訊號狀態,它將始終處於有訊號狀態

/*

在這用ResetEvent把護持物件設定成非訊號狀態是不可以的,因為它存在兩個問題:

1)在單CPU的平臺下,同一時刻只有一個執行緒能夠執行。假設執行緒1先執行WaitForSingleObject(g_hEvent,INFINITE);它通過WaitForSingleObject(g_hEvent,INFINITE);得到了事件物件g_hEvent。但這個時候它的時間片終止了,輪到第二個執行緒執行。因為執行緒1的ResetEvent沒有被執行,所有我們的事件物件仍然處於有訊號狀態。既然它是有訊號狀態,執行緒2就能得到我們這個事件。也就是我們這兩個執行緒都進入了要保護的程式碼,當然結果是不可預料的。

2)在多CPU的平臺下執行緒1和執行緒2可以同時執行。當它們請求到了事件物件後,你再將它們設為非訊號狀態已經不起作用了,因為它們已經進入到我們要保護的程式碼了。兩個執行緒同時訪問同一種資源當然結果是未知的。

*/

if(tickets>0)

{

Sleep(1);

cout<<"thread1 sell ticket : "<<tickets--<<endl;

}

else

break;

SetEvent(g_hEvent);  //將事件物件設定為有訊號狀態

}

return 0;

}

DWORD WINAPI Fun2Proc(

LPVOID lpParameter// thread data

)

{

while(TRUE)

{

WaitForSingleObject(g_hEvent,INFINITE);

//ResetEvent(g_hEvent);

/*

在這用ResetEvent把護持物件設定成非訊號狀態是不可以的,因為它存在兩個問題:

1)在單CPU的平臺下,同一時刻只有一個執行緒能夠執行。假設執行緒1先執行WaitForSingleObject(g_hEvent,INFINITE);它通過WaitForSingleObject(g_hEvent,INFINITE);得到了事件物件g_hEvent。但這個時候它的時間片終止了,輪到第二個執行緒執行。因為執行緒1的ResetEvent沒有被執行,所有我們的事件物件仍然處於有訊號狀態。既然它是有訊號狀態,執行緒2就能得到我們這個事件。也就是我們這兩個執行緒都進入了要保護的程式碼,當然結果是不可預料的。

2)在多CPU的平臺下執行緒1和執行緒2可以同時執行。當它們請求到了事件物件後,你再將它們設為非訊號狀態已經不起作用了,因為它們已經進入到我們要保護的程式碼了。兩個執行緒同時訪問同一種資源當然結果是未知的。

*/

if(tickets>0)

{

Sleep(1);

cout<<"thread2 sell ticket : "<<tickets--<<endl;

}

else

break;

SetEvent(g_hEvent);

}

return 0;

}

 //---------------------------------------------------------------------------飄逸的分割線--------------------------------------------------------------------------

自動重置事件物件例子-----具有實用參考性

/*

自動重置事件物件,當事件物件為有訊號狀態的時候,等待該物件的執行緒只有一個能得到該事件物件,這時候系統會把他變成非訊號狀態,所以在這個執行緒執行結束之後,一定要用SetEvent把該事件物件重新設定成有訊號狀態

*/

#include <windows.h>

#include <iostream.h>

DWORD WINAPI Fun1Proc(

LPVOID lpParameter// thread data

);

DWORD WINAPI Fun2Proc(

LPVOID lpParameter// thread data

);

int tickets=100;

HANDLE g_hEvent;

void main()

{

HANDLE hThread1;

HANDLE hThread2;

hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(hThread1);

CloseHandle(hThread2);

g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);   //第二個引數設定FALSE為自動重置事件物件

SetEvent(g_hEvent);  // 把事件物件設定成有訊號狀態,當然這裡也可以通過把CreateEvent的第三個引數設定成TRUE來實現

Sleep(4000);

CloseHandle(g_hEvent);//關閉事件物件控制代碼

}

DWORD WINAPI Fun1Proc(

LPVOID lpParameter// thread data

)

{

while(TRUE)

{

WaitForSingleObject(g_hEvent,INFINITE); // 這時候系統會把g_hEvent變成非訊號狀態

if(tickets>0)

{

Sleep(1);

cout<<"thread1 sell ticket : "<<tickets--<<endl;

}

else

break;

SetEvent(g_hEvent);//重新設定成有訊號狀態

}

return 0;

}

DWORD WINAPI Fun2Proc(

LPVOID lpParameter// thread data

)

{

while(TRUE)

{

WaitForSingleObject(g_hEvent,INFINITE);

if(tickets>0)

{

Sleep(1);

cout<<"thread2 sell ticket : "<<tickets--<<endl;

}

else

break;

SetEvent(g_hEvent);

}

return 0;

}