CEvent

表示一个事件,即支持一个线程向另一线程通知事件已发生的同步对象。

语法

class CEvent : public CSyncObject

成员

公共构造函数

名称 描述
CEvent::CEvent 构造 CEvent 对象。

公共方法

名称 描述
CEvent::PulseEvent 将事件设置为可用(已发出信号),释放等待线程,然后将事件设置为不可用(未发出信号)。
CEvent::ResetEvent 将事件设置为不可用(未发出信号)。
CEvent::SetEvent 将事件设置为可用(已发出信号)并释放所有等待线程。
CEvent::Unlock 释放事件对象。

备注

当线程必须知道何时执行其任务时,事件很有用。 例如,当有新数据可用时,必须通知将数据复制到数据存档的线程。 当有新数据可用时通过使用 CEvent 对象通知复制线程,该线程可以尽快执行其任务。

CEvent 对象有两种类型:手动和自动。

至少释放一个线程后,自动 CEvent 对象会自动返回到未发出信号(不可用)状态。 默认情况下,除非在构造期间为 bManualReset 参数传递 TRUE,否则 CEvent 对象是自动的。

手动 CEvent 对象保持 SetEventResetEvent 设置的状态,直到调用另一个函数。 若要创建手动 CEvent 对象,请在构造期间为 bManualReset 参数传递 TRUE

若要使用 CEvent 对象,请根据需要构造 CEvent 对象。 指定要等待的事件的名称,并指定应用程序最初应该拥有它。 然后可以在构造函数返回时访问该事件。 调用 SetEvent 以发出事件对象的信号(使其可用),然后在访问完受控资源后调用 Unlock

使用 CEvent 对象的替代方法是将 CEvent 类型的变量作为数据成员添加到要控制的类。 在构造受控对象的过程中,调用 CEvent 数据成员的构造函数并指定是否初始发出事件的信号,另外,指定所需的事件对象类型、事件的名称(如果要跨进程边界使用它),以及所需的任何安全属性。

若要以这种方式访问 CEvent 对象控制的资源,请先在资源的访问方法中创建类型为 CSingleLockCMultiLock 的变量。 然后调用 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
};

继承层次结构

CObject

CSyncObject

CEvent

要求

标头afxmt.h

CEvent::CEvent

构造命名或未命名 CEvent 对象。

CEvent(
    BOOL bInitiallyOwn = FALSE,
    BOOL bManualReset = FALSE,
    LPCTSTR lpszName = NULL,
    LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);

参数

bInitiallyOwn
如果为 TRUE,则启用 CMultilockCSingleLock 对象的线程。 否则,所有想要访问资源的线程都必须等待。

bManualReset
如果为 TRUE,则指定事件对象是手动事件,否则事件对象是自动事件。

lpszName
CEvent 对象的名称。 如果对象将跨进程边界使用,则必须提供。 如果名称与现有事件匹配,构造函数将生成一个新的 CEvent 对象,该对象引用该名称的事件。 如果名称与不是事件的现有同步对象匹配,则构造将会失败。 如果为 NULL,则名称为 null。

lpsaAttribute
事件对象的安全属性。 有关此结构的完整说明,请参阅 Windows SDK 中的 SECURITY_ATTRIBUTES

备注

若要访问或释放 CEvent 对象,请创建一个 CMultiLockCSingleLock 对象并调用其 LockUnlock 成员函数。

若要将 CEvent 对象的状态更改为已发出信号(线程不必等待),请调用 SetEventPulseEvent。 若要将 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 对象的析构函数调用。

另请参阅

CSyncObject
层次结构图