Using Event Objects

Event kernel objects are used to allow a thread to block until another thread has completed a task. For example, one thread may be reading data from the Internet, and other threads can use an event to block until all the data has been read. Events can either be "manual-reset" or "auto-reset", and the type of event affects how threads blocking on the event behave.

  • Manual-Reset Events: When the event becomes signaled through a thread calling SetEvent, all threads blocking on the event will be unblocked. The event remains signaled until any thread calls ResetEvent at which point the event becomes non-signaled.

  • Auto-Reset Events: When the event becomes signaled through a thread calling SetEvent, only one thread blocking on the event will be unblocked, at which point the event will automatically become non-signaled.

Events are created through a call to CreateEvent (Table 6.5). This function allows both manual-reset and auto-reset events to be created with either a signaled or non-signaled state. Unlike mutex objects, events are not owned by a thread, so any thread can change the signaled state once it has a handle to the event. As with all kernel objects, CloseHandle should be called on the event handle when it is finished with.

Table 6.5. CreateEvent—Creates a new event or opens an existing event
CreateEvent
LPSECURITY_ATTRIBUTES lpMutexAttributesNot supported, pass as NULL.
BOOL bManualResetTRUE to create a manual-reset event, or FALSE for an auto-reset event.
BOOL bInitialStateTRUE if the event is to be initially signaled, or FALSE if the event is to be initially non-signaled.
LPTSTR lpNameString containing name of event, or NULL if an unnamed event is being created. If this parameter is NULL a new event is always created.
HANDLE Return ValueHandle to new or existing event, or NULL on failure. GetLastError returns ERROR_ALREADY_EXISTS if an existing mutex was opened.

The only way to change an event's state to signaled is to call SetEvent. This function takes a single argument that is the handle to the event, and returns TRUE on success, or FALSE for failure. Threads don't need to explicitly change an event's state to non-signaled since this happens automatically when the first thread unblocks. Manual events can be set to non-signaled through calling the ResetEvent function. This function takes a single argument that is the handle to the event and returns a Boolean indicating success or failure.

A third function, PulseEvent, is used primarily with manual events. This function sets an event's state to signaled, and then immediately sets it to non-signaled. All threads that are blocked on the event are unblocked. However, any threads that subsequently call WaitForSingleObject will block on the event until either PulseEvent or SetEvent are called.

As an example of when an event may be used, consider the code in Listing 6.3. The function Listing6_3 declares a local variable structure "threadInfo", initializes the structure, and passes a pointer to a new thread through the CreateThread function. The thread function takes a copy of the structure pointed to by lpThreadInfo into a local structure variable called tInfo. Surprisingly, this thread function fails most of the time, since the thread function receives garbage in the structure that is passed to it. This is a classic synchronization problem—the function that creates the threads returns and the stack space occupied by the structure is reused when the thread goes on to call other functions. By the time the thread does execute, its pointer refers to a structure that is long gone (Figure 6.5).

Figure 6.5. Problem passing structure pointer to thread function


Listing 6.3. Thread creation that requires an event for synchronization
typedef struct tagTHREADINFO {
    DWORD dwVal1, dwVal2;
} THREADINFO, *LPTHREADINFO;
DWORD WINAPI ThreadFunc(LPVOID lpThreadInfo);
void Listing6_3()
{
  THREADINFO threadInfo;
  HANDLE hThread;
  DWORD dwThreadId;
  threadInfo.dwVal1 = 20;
  threadInfo.dwVal2 = 40;
  hThread = CreateThread(NULL, 0, ThreadFunc,
        (LPVOID)&threadInfo, 0, &dwThreadId);
  CloseHandle(hThread);
}
DWORD ThreadFunc(LPVOID lpThreadInfo)
{
  LONG lResult;
  THREADINFO tInfo = *((LPTHREADINFO)lpThreadInfo);
  lResult = tInfo.dwVal1 * tInfo.dwVal2;
  cout ≪ _T("Result: ") ≪ lResult  ≪ endl;
  return 0;
}

This problem can be fixed by creating a non-signaled event in the function Listing3_4, and having the Listing3_4 function block after creating the thread. The thread function ThreadFunc can then signal the event once it has taken a copy of the structure. This is shown in Listing 6.4, with the lines of code added for synchronization shown in bold. The event has its signal state changed just once with only a single thread waiting on it. Therefore, it does not matter if an auto-reset or manual-reset event is created. Figure 6.6 shows the program flow with this corrected code.

Figure 6.6. Synchronized code for creating thread


Listing 6.4. Thread creation requiring an event for synchronization
typedef struct tagTHREADINFO {
  DWORD dwVal1, dwVal2;
}THREADINFO, *LPTHREADINFO;
DWORD WINAPI ThreadFunc2(LPVOID lpThreadInfo);
HANDLE hEvent;
void Listing6_4()
{
  THREADINFO threadInfo;
  HANDLE hThread;
  DWORD dwThreadId;
  threadInfo.dwVal1 = 20;
  threadInfo.dwVal2 = 40;
  hEvent = CreateEvent(NULL,
					TRUE,   // manual event
					FALSE,  // initially non-signaled
					NULL);  // no name
  hThread = CreateThread(NULL, 0, ThreadFunc2,
        (LPVOID)&threadInfo, 0, &dwThreadId);
  WaitForSingleObject(hEvent, INFINITE);
  CloseHandle(hEvent);
  CloseHandle(hThread);
}
DWORD ThreadFunc2(LPVOID lpThreadInfo)
{
  LONG lResult;
  THREADINFO tInfo = *((LPTHREADINFO)lpThreadInfo);
  // Unblock Listing6_4 now the structure has been copied
  SetEvent(hEvent);
  lResult = tInfo.dwVal1 * tInfo.dwVal2;
  cout  _T("Result: ")  ≪ lResult  ≪ endl;
  return 0;
}

Events can be named through the last parameter to CreateEvent. This allows an event to synchronize threads in different processes. Windows CE does not support the OpenEvent function, but CreateEvent can be passed the name of an existing event and it will be opened. GetLastError will return ERROR_ALREADY_EXISTS.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.65.65