CEvent
类
表示一个事件,即支持一个线程向另一线程通知事件已发生的同步对象。
语法
class CEvent : public CSyncObject
成员
公共构造函数
名称 | 描述 |
---|---|
CEvent::CEvent |
构造 CEvent 对象。 |
公共方法
名称 | 描述 |
---|---|
CEvent::PulseEvent |
将事件设置为可用(已发出信号),释放等待线程,然后将事件设置为不可用(未发出信号)。 |
CEvent::ResetEvent |
将事件设置为不可用(未发出信号)。 |
CEvent::SetEvent |
将事件设置为可用(已发出信号)并释放所有等待线程。 |
CEvent::Unlock |
释放事件对象。 |
备注
当线程必须知道何时执行其任务时,事件很有用。 例如,当有新数据可用时,必须通知将数据复制到数据存档的线程。 当有新数据可用时通过使用 CEvent
对象通知复制线程,该线程可以尽快执行其任务。
CEvent
对象有两种类型:手动和自动。
至少释放一个线程后,自动 CEvent
对象会自动返回到未发出信号(不可用)状态。 默认情况下,除非在构造期间为 bManualReset
参数传递 TRUE
,否则 CEvent
对象是自动的。
手动 CEvent
对象保持 SetEvent
或 ResetEvent
设置的状态,直到调用另一个函数。 若要创建手动 CEvent
对象,请在构造期间为 bManualReset
参数传递 TRUE
。
若要使用 CEvent
对象,请根据需要构造 CEvent
对象。 指定要等待的事件的名称,并指定应用程序最初应该拥有它。 然后可以在构造函数返回时访问该事件。 调用 SetEvent
以发出事件对象的信号(使其可用),然后在访问完受控资源后调用 Unlock
。
使用 CEvent
对象的替代方法是将 CEvent
类型的变量作为数据成员添加到要控制的类。 在构造受控对象的过程中,调用 CEvent
数据成员的构造函数并指定是否初始发出事件的信号,另外,指定所需的事件对象类型、事件的名称(如果要跨进程边界使用它),以及所需的任何安全属性。
若要以这种方式访问 CEvent
对象控制的资源,请先在资源的访问方法中创建类型为 CSingleLock
或 CMultiLock
的变量。 然后调用 lock
对象的 Lock
方法(例如 CMultiLock::Lock
)。 此时,线程将获取对资源的访问权限,等待资源释放并获取访问权限,或等待资源释放并超时而无法访问资源。 在任何情况下,都是以线程安全的方式访问资源。 若要释放资源,请调用 SetEvent
以发出事件对象的信号,然后使用 lock
对象的 Unlock
方法(例如 CMultiLock::Unlock
),或者让 lock
对象超出范围。
有关如何使用 CEvent
对象的详细信息,请参阅多线程:如何使用同步类。
示例
// 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
};
继承层次结构
CEvent
要求
标头:afxmt.h
CEvent::CEvent
构造命名或未命名 CEvent
对象。
CEvent(
BOOL bInitiallyOwn = FALSE,
BOOL bManualReset = FALSE,
LPCTSTR lpszName = NULL,
LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);
参数
bInitiallyOwn
如果为 TRUE
,则启用 CMultilock
或 CSingleLock
对象的线程。 否则,所有想要访问资源的线程都必须等待。
bManualReset
如果为 TRUE
,则指定事件对象是手动事件,否则事件对象是自动事件。
lpszName
CEvent
对象的名称。 如果对象将跨进程边界使用,则必须提供。 如果名称与现有事件匹配,构造函数将生成一个新的 CEvent
对象,该对象引用该名称的事件。 如果名称与不是事件的现有同步对象匹配,则构造将会失败。 如果为 NULL
,则名称为 null。
lpsaAttribute
事件对象的安全属性。 有关此结构的完整说明,请参阅 Windows SDK 中的 SECURITY_ATTRIBUTES
。
备注
若要访问或释放 CEvent
对象,请创建一个 CMultiLock
或 CSingleLock
对象并调用其 Lock
和 Unlock
成员函数。
若要将 CEvent
对象的状态更改为已发出信号(线程不必等待),请调用 SetEvent
或 PulseEvent
。 若要将 CEvent
对象的状态设置为未发出信号(线程必须等待),请调用 ResetEvent
。
重要
创建 CEvent
对象后,使用 GetLastError
确保互斥不存在。 如果 mutex 意外存在,这可能指示流氓进程正在占用 mutex,并可能打算恶意使用它。 在这种情况下,推荐采用有安全意识的做法,即关闭句柄并继续,就像创建对象时发生故障。
CEvent::PulseEvent
将事件的状态设置为已发出信号(可用),释放所有等待的线程,然后自动将其重置为未发出信号(不可用)。
BOOL PulseEvent();
返回值
如果该函数成功,则为非零;否则为 0。
备注
如果事件是手动事件,则释放所有等待的线程,将事件设置为未发出信号,然后 PulseEvent
返回。 如果事件是自动事件,则释放单个线程,将事件设置为未发出信号,然后 PulseEvent
返回。
如果没有任何线程处于等待中状态,或者没有任何线程可立即释放,则 PulseEvent
会将事件的状态设置为未发出信号并返回。
PulseEvent
使用基础 Win32 PulseEvent
函数,可以通过内核模式异步过程调用即时将该函数从等待状态中删除。 因此 PulseEvent
并不可靠,不应由新应用程序使用。 有关详细信息,请参阅 PulseEvent
函数。
CEvent::ResetEvent
将事件的状态设置为未发出信号,直到 SetEvent
成员函数显式将状态设置为已发出信号。
BOOL ResetEvent();
返回值
如果该函数成功,则为非零;否则为 0。
备注
这会导致所有希望访问此事件的线程等待。
自动事件不使用此成员函数。
CEvent::SetEvent
将事件的状态设置为已发出信号,并释放所有等待的线程。
BOOL SetEvent();
返回值
如果该函数成功,则返回非零值,否则返回 0。
备注
如果事件是手动事件,则在调用 ResetEvent
之前,事件将保持已发出信号状态。 在这种情况下可以释放多个线程。 如果事件是自动事件,则事件将保持已发出信号状态,直到释放单个线程。 然后系统会将事件的状态设置为未发出信号。 如果没有任何线程处于等待中状态,则保持已发出信号状态,直到释放一个线程。
CEvent::Unlock
释放事件对象。
BOOL Unlock();
返回值
如果线程拥有事件对象并且事件是自动事件,则返回非零值;否则返回 0。
注解
如果要重用其 lock
对象,则当前拥有自动事件的线程将调用此成员函数以在完成后释放它。 如果不重复使用 lock
对象,则此函数将由 lock
对象的析构函数调用。