33 out of 47 rated this helpful Rate this topic

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

Did you find this helpful?
(2000 characters remaining)
Community Content Add
Annotations FAQ
Implementation with print on the console
#include <windows.h>
#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;
}
Re to freemail165
I think here the reset is not important, it works it
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?
who will call ResetEvent() in you program?
you used manual-reset event,and it may wake up all the threads wait for it.

under this situation,who will call ResetEvent()?