Using Event Objects Program Example
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. 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.
Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.
Then, add the source file and give it a suitable name.
Next, add the following source code.
#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
LMyWriteEventGedik // object name
);
if (ghWriteEvent == NULL)
{
wprintf(LCreateEvent() failed, error %d\n, GetLastError());
return;
}
else
wprintf(LCreateEvent() is OK...\n);
// 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)
{
wprintf(LCreateThread() failed, error %d\n, GetLastError());
return;
}
else
wprintf(LCreateThread() for thread #%d is OK!\n, i);
}
}
void WriteToBuffer(void)
{
// TODO: Write to the shared buffer.
wprintf(LMain thread writing to the shared buffer...\n);
// Set ghWriteEvent to signaled
if (! SetEvent(ghWriteEvent) )
{
wprintf(L\nSetEvent() failed, error %d\n, GetLastError());
return;
}
else
wprintf(L\nSetEvent() is OK!\n);
}
void CloseEvents()
{
// Close all event handles (currently, only one global handle).
if(CloseHandle(ghWriteEvent) != 0)
wprintf(LClosing the ghWriteEvent's handle is OK\n);
else
wprintf(LFail to close ghWriteEvent's handle, error %d\n, GetLastError());
}
void wmain()
{
DWORD dwWaitResult;
wprintf(LThe main() process started...\n);
wprintf(L With thread #%d\n, GetCurrentThreadId());
// 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();
wprintf(LThe 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:
wprintf(LAll threads ended, cleaning up for application exit...\n);
break;
// An error occurred
default:
wprintf(LWaitForMultipleObjects failed (%d)\n, GetLastError());
return;
}
// Close the events to clean up
CloseEvents();
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult;
wprintf(LThread %d waiting for the 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
wprintf(LThread %d reading from buffer...\n, GetCurrentThreadId());
break;
// An error occurred
default:
wprintf(LWaitForSingleObject() 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.)
wprintf(LThread %d exiting...\n, GetCurrentThreadId());
return 1;
}
Build and run the project. The following screenshot is a sample output.