CEvent 类

注释

Microsoft基础类(MFC)库将继续受到支持。 但是,我们不再添加功能或更新文档。

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

语法

class CEvent : public CSyncObject

成员

公共构造函数

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

公共方法

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

备注

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

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

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

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

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

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

若要以这种方式访问 CEvent 对象控制的资源,请先在资源的访问方法中创建类型为 CSingleLock 或 CMultiLock 的变量。 然后调用 Lock 对象的 lock 方法(例如 CMultiLock::Lock)。 此时,线程将获取对资源的访问权限,等待资源释放并获取访问权限,或等待资源释放并超时而无法访问资源。 在任何情况下,都是以线程安全的方式访问资源。 若要释放资源,请调用 SetEvent 以发出事件对象的信号,然后使用 Unlock 对象的 lock 方法(例如 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,则启用 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 对象的析构函数调用。

另请参阅

CSyncObject 类
层次结构图