Using Event Objects
Applications can use event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, overlapped I/O operations on files, named pipes, and communications devices use an event object to signal their completion. For more information about the use of event objects in overlapped I/O operations, see Synchronization and Overlapped Input and Output.
The following example uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent function to create a manual-reset event object whose initial state is nonsignaled. Then it creates several reader threads. The master thread performs a write operation and then sets the event object to the signaled state when it has finished writing.
Before starting a read operation, each reader thread uses WaitForSingleObject to wait for the manual-reset event object to be signaled. When WaitForSingleObject returns, this indicates that the main thread is ready for it to begin its read operation.
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
HANDLE ghWriteEvent;
HANDLE ghThreads[THREADCOUNT];
DWORD WINAPI ThreadProc(LPVOID);
void CreateEventsAndThreads(void)
{
int i;
DWORD dwThreadID;
// Create a manual-reset event object. The write thread sets this
// object to the nonsignaled state when it finishes writing to a
// shared buffer.
ghWriteEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if (ghWriteEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create multiple threads to read from the buffer.
for(i = 0; i < THREADCOUNT; i++)
{
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
ghThreads[i] = CreateThread(
NULL, // default security
0, // default stack size
ThreadProc, // name of the thread function
NULL, // no thread parameters
0, // default startup flags
&dwThreadID);
if (ghThreads[i] == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
}
void WriteToBuffer(VOID)
{
// TODO: Write to the shared buffer.
printf("Main thread writing to the shared buffer...\n");
// Set ghWriteEvent to signaled
if (! SetEvent(ghWriteEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return;
}
}
void CloseEvents()
{
// Close all event handles (currently, only one global handle).
CloseHandle(ghWriteEvent);
}
int main( void )
{
DWORD dwWaitResult;
// TODO: Create the shared buffer
// Create events and THREADCOUNT threads to read from the buffer
CreateEventsAndThreads();
// At this point, the reader threads have started and are most
// likely waiting for the global event to be signaled. However,
// it is safe to write to the buffer because the event is a
// manual-reset event.
WriteToBuffer();
printf("Main thread waiting for threads to exit...\n");
// The handle for each thread is signaled when the thread is
// terminated.
dwWaitResult = WaitForMultipleObjects(
THREADCOUNT, // number of handles in array
ghThreads, // array of thread handles
TRUE, // wait until all are signaled
INFINITE);
switch (dwWaitResult)
{
// All thread objects were signaled
case WAIT_OBJECT_0:
printf("All threads ended, cleaning up for application exit...\n");
break;
// An error occurred
default:
printf("WaitForMultipleObjects failed (%d)\n", GetLastError());
return 1;
}
// Close the events to clean up
CloseEvents();
return 0;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
// lpParam not used in this example.
UNREFERENCED_PARAMETER(lpParam);
DWORD dwWaitResult;
printf("Thread %d waiting for write event...\n", GetCurrentThreadId());
dwWaitResult = WaitForSingleObject(
ghWriteEvent, // event handle
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// Event object was signaled
case WAIT_OBJECT_0:
//
// TODO: Read from the shared buffer
//
printf("Thread %d reading from buffer\n",
GetCurrentThreadId());
break;
// An error occurred
default:
printf("Wait error (%d)\n", GetLastError());
return 0;
}
// Now that we are done reading the buffer, we could use another
// event to signal that this thread is no longer reading. This
// example simply uses the thread handle for synchronization (the
// handle is signaled when the thread terminates.)
printf("Thread %d exiting\n", GetCurrentThreadId());
return 1;
}
Send comments about this topic to Microsoft
Build date: 9/7/2011
#include <stdio.h>
#define THREADCOUNT 5
#define THREADLOOP 10000
HANDLE ghWriteEvent;
HANDLE ghThreads[THREADCOUNT];
DWORD WINAPI ThreadProc(LPVOID);
void gotoXY(int x, int y);
CRITICAL_SECTION CriticalSection;
int ai[THREADCOUNT+1] = { 0 };
void CreateEventsAndThreads(void)
{
int i;
DWORD dwThreadID;
// Create a manual-reset event object. The write thread sets this
// object to the nonsignaled state when it finishes writing to a
// shared buffer.
ghWriteEvent = CreateEvent(
NULL, // default security attributes
FALSE, // manual-reset event
TRUE, // initial state
TEXT("WriteEvent") // object name
);
if (ghWriteEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create multiple threads to read from the buffer.
int j = 1;
for(i = 0; i < THREADCOUNT; i++, j+=4)
{
ai [ i ] = j;
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
ghThreads[i] = CreateThread(
NULL, // default security
0, // default stack size
ThreadProc, // name of the thread function
& ai [ i ], // no thread parameters
0, // default startup flags
&dwThreadID);
if (ghThreads[i] == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
ai[THREADCOUNT] = j;
}
int main( void )
{
DWORD dwWaitResult;
// TODO: Create the shared buffer
InitializeCriticalSection(&CriticalSection);
printf("Main thread waiting for threads to exit...\n");
// Create events and THREADCOUNT threads to read from the buffer
CreateEventsAndThreads();
// The handle for each thread is signaled when the thread is
// terminated.
dwWaitResult = WaitForMultipleObjects(
THREADCOUNT, // number of handles in array
ghThreads, // array of thread handles
TRUE, // wait until all are signaled
INFINITE);
EnterCriticalSection(&CriticalSection);
gotoXY(0, ai[THREADCOUNT]+2);
switch (dwWaitResult)
{
// All thread objects were signaled
case WAIT_OBJECT_0:
printf("All threads ended, cleaning up for application exit...\n");
break;
// An error occurred
default:
printf("WaitForMultipleObjects failed (%d)\n", GetLastError());
return 1;
}
LeaveCriticalSection(&CriticalSection);
// Close the events to clean up
CloseHandle(ghWriteEvent);
return 0;
}
void ChangeColour(WORD theColour)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole,theColour);
}
void gotoXY(int x, int y)
{
//Initialize the coordinates
COORD coord = {x, y};
//Set the position
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
return;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
// lpParam not used in this example.
//UNREFERENCED_PARAMETER(lpParam);
int i = *(int *)lpParam;
DWORD dwWaitResult;
int wait = 0;
int read = 0;
ChangeColour(FOREGROUND_GREEN);
int loop = 0;
for ( ; loop < THREADLOOP; loop++ )
{
while (1)
{
wait++;
if ( (dwWaitResult = WaitForSingleObject( ghWriteEvent,0 )) == WAIT_OBJECT_0 )
{
//ResetEvent(ghWriteEvent);
break;
}
EnterCriticalSection(&CriticalSection);
gotoXY(0,i+1);
printf("[%02d]Thread %05d waiting %05d times for write event...", i,GetCurrentThreadId(), wait );
LeaveCriticalSection(&CriticalSection);
}
EnterCriticalSection(&CriticalSection);
gotoXY(0,i+2);
switch (dwWaitResult)
{
// Event object was signaled
case WAIT_OBJECT_0:
//
// TODO: Read from the shared buffer
//
read++;
printf("----Thread %05d reading %05d times from buffer", GetCurrentThreadId(), read);
break;
// An error occurred
default:
printf("Wait error (%d)", GetLastError());
return 0;
}
LeaveCriticalSection(&CriticalSection);
SetEvent(ghWriteEvent);
}
// Now that we are done reading the buffer, we could use another
// event to signal that this thread is no longer reading. This
// example simply uses the thread handle for synchronization (the
// handle is signaled when the thread terminates.)
EnterCriticalSection(&CriticalSection);
gotoXY(0,i+3);
printf(" Thread %05d exiting\n", GetCurrentThreadId());
LeaveCriticalSection(&CriticalSection);
return 1;
}
- 6/10/2011
- lSalamon
this example.
But I have my own case where I have to use reset or
automatic reset for an event. There two threads that wait for this event. Each
of them must to react to this event only once. The problem is that once one
thread has reset the event, the other thread will not be notified about this
event... My code waits for multiple events in each thread, so I have to reset
this event to not trip over it again and again in the waiting loop...
Can I edit somebody else comments or is it just my impression?
- 2/27/2011
- Darius Pl
