CEvent
Class
Represents an event, which is a synchronization object that enables one thread to notify another that an event has occurred.
Syntax
class CEvent : public CSyncObject
Members
Public Constructors
Name | Description |
---|---|
CEvent::CEvent |
Constructs a CEvent object. |
Public Methods
Name | Description |
---|---|
CEvent::PulseEvent |
Sets the event to available (signaled), releases waiting threads, and sets the event to unavailable (nonsignaled). |
CEvent::ResetEvent |
Sets the event to unavailable (nonsignaled). |
CEvent::SetEvent |
Sets the event to available (signaled) and releases any waiting threads. |
CEvent::Unlock |
Releases the event object. |
Remarks
Events are useful when a thread must know when to perform its task. For example, a thread that copies data to a data archive must be notified when new data is available. By using a CEvent
object to notify the copy thread when new data is available, the thread can perform its task as soon as possible.
CEvent
objects have two types: manual and automatic.
An automatic CEvent
object automatically returns to a non-signaled (unavailable) state after at least one thread is released. By default, a CEvent
object is automatic unless you pass TRUE
for the bManualReset
parameter during construction.
A manual CEvent
object stays in the state set by SetEvent
or ResetEvent
until the other function is called. To create a manual CEvent
object, pass TRUE
for the bManualReset
parameter during construction.
To use a CEvent
object, construct the CEvent
object when it's required. Specify the name of the event you want to wait on, and also specify that your application should initially own it. You can then access the event when the constructor returns. Call SetEvent
to signal (make available) the event object and then call Unlock
when you're done accessing the controlled resource.
An alternative method for using CEvent
objects is to add a variable of type CEvent
as a data member to the class you want to control. During construction of the controlled object, call the constructor of the CEvent
data member and specify whether the event is initially signaled, and also specify the type of event object you want, the name of the event (if it will be used across process boundaries), and any security attributes you want.
To access a resource controlled by a CEvent
object in this manner, first create a variable of either type CSingleLock
or type CMultiLock
in the access method of your resource. Then call the Lock
method of the lock
object (for example, CMultiLock::Lock
). At this point, your thread will either gain access to the resource, wait for the resource to be released and gain access, or wait for the resource to be released, time out, and fail to gain access to the resource. In any case, your resource has been accessed in a thread-safe manner. To release the resource, call SetEvent
to signal the event object, and then use the Unlock
method of the lock
object (for example, CMultiLock::Unlock
), or let the lock
object fall out of scope.
For more information about how to use CEvent
objects, see Multithreading: How to Use the Synchronization Classes.
Example
// The following demonstrates trivial usage of the CEvent class.
// A CEvent object is created and passed as a parameter to another
// thread. The other thread will wait for the event to be signaled
// and then exit
UINT __cdecl MyThreadProc(LPVOID lpParameter)
{
CEvent *pEvent = (CEvent *)(lpParameter);
VERIFY(pEvent != NULL);
// Wait for the event to be signaled
::WaitForSingleObject(pEvent->m_hObject, INFINITE);
// Terminate the thread
::AfxEndThread(0, FALSE);
return 0L;
}
void CEvent_Test()
{
// Create the CEvent object that will be passed to the thread routine
CEvent *pEvent = new CEvent(FALSE, FALSE);
// Create a thread that will wait on the event
CWinThread *pThread;
pThread = ::AfxBeginThread(&MyThreadProc, pEvent, 0, 0, CREATE_SUSPENDED, NULL);
pThread->m_bAutoDelete = FALSE;
pThread->ResumeThread();
// Signal the thread to do the next work item
pEvent->SetEvent();
// Wait for the thread to consume the event and return
::WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
delete pEvent;
}
// This example builds upon the previous one.
// A second thread is created to calculate prime numbers.
// The main thread will signal the second thread to calculate the next
// prime number in the series. The second thread signals the first
// after each number is calculated. Finally, after several iterations
// the worker thread is signaled to terminate.
class CPrimeTest
{
public:
CPrimeTest()
: m_pCalcNext(new CEvent(FALSE, FALSE)),
m_pCalcFinished(new CEvent(FALSE, FALSE)),
m_pTerminateThread(new CEvent(FALSE, FALSE)),
m_iCurrentPrime(0)
{
// Create a thread that will calculate the prime numbers
CWinThread *pThread;
pThread = ::AfxBeginThread(&PrimeCalcProc,
this, 0, 0, CREATE_SUSPENDED, NULL);
pThread->m_bAutoDelete = FALSE;
pThread->ResumeThread();
// Calcuate the first 10 prime numbers in the series on the thread
for (UINT i = 0; i < 10; i++)
{
// Signal the thread to do the next work item
m_pCalcNext->SetEvent();
// Wait for the thread to complete the current task
::WaitForSingleObject(m_pCalcFinished->m_hObject, INFINITE);
// Print the result
TRACE(_T("The value of m_iCurrentPrime is: %d\n"), m_iCurrentPrime);
}
// Notify the worker thread to exit and wait for it to complete
m_pTerminateThread->SetEvent();
::WaitForSingleObject(pThread->m_hThread, INFINITE);
delete pThread;
}
~CPrimeTest()
{
delete m_pCalcNext;
delete m_pCalcFinished;
delete m_pTerminateThread;
}
private:
// Determines whether the given number is a prime number
static BOOL IsPrime(INT ThisPrime)
{
if (ThisPrime < 2)
return FALSE;
for (INT n = 2; n < ThisPrime; n++)
{
if (ThisPrime % n == 0)
return FALSE;
}
return TRUE;
}
// Calculates the next prime number in the series
static INT NextPrime(INT ThisPrime)
{
while (TRUE)
{
if (IsPrime(++ThisPrime))
{
return ThisPrime;
}
}
}
// Worker thread responsible for calculating the next prime
// number in the series
static UINT __cdecl PrimeCalcProc(LPVOID lpParameter)
{
CPrimeTest *pThis = static_cast<CPrimeTest *>(lpParameter);
VERIFY(pThis != NULL);
VERIFY(pThis->m_pCalcNext != NULL);
VERIFY(pThis->m_pCalcFinished != NULL);
VERIFY(pThis->m_pTerminateThread != NULL);
// Create a CMultiLock object to wait on the various events
// WAIT_OBJECT_0 refers to the first event in the array,
// WAIT_OBJECT_0+1 refers to the second
CSyncObject *pWaitObjects[] = {pThis->m_pCalcNext,
pThis->m_pTerminateThread};
CMultiLock MultiLock(pWaitObjects, 2L);
while (MultiLock.Lock(INFINITE, FALSE) == WAIT_OBJECT_0)
{
// Calculate next prime
pThis->m_iCurrentPrime = NextPrime(pThis->m_iCurrentPrime);
// Notify main thread calculation is complete
pThis->m_pCalcFinished->SetEvent();
}
// Terminate the thread
::AfxEndThread(0, FALSE);
return 0L;
}
CEvent *m_pCalcNext; // notifies worker thread to calculate next prime
CEvent *m_pCalcFinished; // notifies main thread current calculation is complete
CEvent *m_pTerminateThread; // notifies worker thread to terminate
INT m_iCurrentPrime; // current calculated prime number
};
Inheritance Hierarchy
CEvent
Requirements
Header: afxmt.h
CEvent::CEvent
Constructs a named or unnamed CEvent
object.
CEvent(
BOOL bInitiallyOwn = FALSE,
BOOL bManualReset = FALSE,
LPCTSTR lpszName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);
Parameters
bInitiallyOwn
If TRUE
, the thread for the CMultilock
or CSingleLock
object is enabled. Otherwise, all threads wanting to access the resource must wait.
bManualReset
If TRUE
, specifies that the event object is a manual event, otherwise the event object is an automatic event.
lpszName
Name of the CEvent
object. Must be supplied if the object will be used across process boundaries. If the name matches an existing event, the constructor builds a new CEvent
object that references the event of that name. If the name matches an existing synchronization object that isn't an event, the construction will fail. If NULL
, the name will be null.
lpsaAttribute
Security attributes for the event object. For a full description of this structure, see SECURITY_ATTRIBUTES
in the Windows SDK.
Remarks
To access or release a CEvent
object, create a CMultiLock
or CSingleLock
object and call its Lock
and Unlock
member functions.
To change the state of a CEvent
object to signaled (threads don't have to wait), call SetEvent
or PulseEvent
. To set the state of a CEvent
object to nonsignaled (threads must wait), call ResetEvent
.
Important
After creating the CEvent
object, use GetLastError
to ensure that the mutex didn't already exist. If the mutex did exist unexpectedly, it may indicate a rogue process is squatting and may be intending to use the mutex maliciously. In this case, the recommended security-conscious procedure is to close the handle and continue as if there was a failure in creating the object.
CEvent::PulseEvent
Sets the state of the event to signaled (available), releases any waiting threads, and resets it to nonsignaled (unavailable) automatically.
BOOL PulseEvent();
Return Value
Nonzero if the function was successful; otherwise 0.
Remarks
If the event is manual, all waiting threads are released, the event is set to nonsignaled, and PulseEvent
returns. If the event is automatic, a single thread is released, the event is set to nonsignaled, and PulseEvent
returns.
If no threads are waiting, or no threads can be released immediately, PulseEvent
sets the state of the event to nonsignaled and returns.
PulseEvent
uses the underlying Win32 PulseEvent
function, which can be momentarily removed from the wait state by a kernel-mode asynchronous procedure call. Therefore, PulseEvent
is unreliable and shouldn't be used by new applications. For more information, see the PulseEvent
function.
CEvent::ResetEvent
Sets the state of the event to nonsignaled until explicitly set to signaled by the SetEvent
member function.
BOOL ResetEvent();
Return Value
Nonzero if the function was successful; otherwise 0.
Remarks
This causes all threads wishing to access this event to wait.
This member function isn't used by automatic events.
CEvent::SetEvent
Sets the state of the event to signaled, releasing any waiting threads.
BOOL SetEvent();
Return Value
Nonzero if the function was successful, otherwise 0.
Remarks
If the event is manual, the event will remain signaled until ResetEvent
is called. More than one thread can be released in this case. If the event is automatic, the event will remain signaled until a single thread is released. The system will then set the state of the event to nonsignaled. If no threads are waiting, the state remains signaled until one thread is released.
CEvent::Unlock
Releases the event object.
BOOL Unlock();
Return Value
Nonzero if the thread owned the event object and the event is an automatic event; otherwise 0.
Remarks
This member function is called by threads that currently own an automatic event to release it after they're done, if their lock
object is to be reused. If the lock
object isn't to be reused, this function will be called by the lock
object's destructor.