讨论

一两种方式创建,一种为自动重置,在其他线程使用WaitForSingleObject等待到事件对象变为有信号后该事件对象自动又变为无信号状态,一种为人工重置在其他线程使用WaitForSingleObject等待到事件对象变为有信号后该事件对象状态不变。例如有多个线程都在等待一个线程运行结束,我们就可以使用人工重置事件,在被等待的线程结束时设置该事件为有信号状态,这样其他的多个线程对该事件的等待都会成功(因为该事件的状态不会被自动重置)。事件相关的API如下: 

创建事件对象:
HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全属性,NULL表示使用默认的安全描述
  BOOL bManualReset,  // 是否为人工重置
  BOOL bInitialState, // 初始状态是否为有信号状态
  LPCTSTR lpName      // 名字
);
打开事件对象:
HANDLE OpenEvent(
  DWORD dwDesiredAccess,  // 存取方式
  BOOL bInheritHandle,    // 是否能够被继承
  LPCTSTR lpName          // 名字
);
设置事件为无信号状态:
BOOL ResetEvent(
  HANDLE hEvent   // 句柄
);
设置事件有无信号状态:
BOOL SetEvent(
  HANDLE hEvent   // 句柄
);
关闭事件对象:
BOOL CloseHandle(
  HANDLE hObject   // 句柄
);

下面的代码演示了自动重置和人工重置事件在使用中的不同效果: 

DWORD threadA(void* pD)
{
int iID=(int)pD;
//在内部重新打开
HANDLE hCounterIn=OpenEvent(EVENT_ALL_ACCESS,FALSE,"sam sp 44");

printf(" thread %d begin ",iID);
//设置成为有信号状态
Sleep(1000);
SetEvent(hCounterIn);
Sleep(1000);
printf(" thread %d end ",iID);
CloseHandle(hCounterIn);
return 0;
}

DWORD threadB(void* pD)
{//等待threadA结束后在继续执行
int iID=(int)pD;
//在内部重新打开
HANDLE hCounterIn=OpenEvent(EVENT_ALL_ACCESS,FALSE,"sam sp 44");

if(WAIT_TIMEOUT == WaitForSingleObject(hCounterIn,10*1000))
{
printf(" thread %d wait time out ",iID);
}
else
{
printf(" thread %d wait ok ",iID);
}
CloseHandle(hCounterIn);
return 0;
}

//in main function
{
HANDLE hCounter=NULL;
if( (hCounter=OpenEvent(EVENT_ALL_ACCESS,FALSE,"sam sp 44"))==NULL)
{
//如果没有其他进程创建这个事件,则重新创建,该事件为人工重置事件
hCounter = CreateEvent(NULL,TRUE,FALSE,"sam sp 44");
}

//创建线程
HANDLE hThread[3];
printf("test of manual rest event ");
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)threadA,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)threadB,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)threadB,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
//等待线程结束
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
//关闭句柄
CloseHandle(hCounter);

if( (hCounter=OpenEvent(EVENT_ALL_ACCESS,FALSE,"sam sp 44"))==NULL)
{
//如果没有其他进程创建这个事件,则重新创建,该事件为自动重置事件
hCounter = CreateEvent(NULL,FALSE,FALSE,"sam sp 44");
}
//创建线程
printf("test of auto rest event ");
pT1=AfxBeginThread((AFX_THREADPROC)threadA,(void*)1);
pT2=AfxBeginThread((AFX_THREADPROC)threadB,(void*)2);
pT3=AfxBeginThread((AFX_THREADPROC)threadB,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
//等待线程结束
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
//关闭句柄
CloseHandle(hCounter);
}

从执行结果中我们可以看到在第二次执行时由于使用了自动重置事件threadB中只有一个线程能够等待到threadA中释放的事件对象。 

在处理多进程/线程的同步问题时必须要小心避免发生死锁问题,比如说现在有两个互斥量A和B,两个线程tA和tB,他们在执行前都需要得到这两个互斥量,但现在这种情况发生了,tA拥有了互斥量A,tB拥有了互斥量B,但它们同时都在等待拥有另一个互斥量,这时候显然谁也不可能得到自己希望的资源。这种互相拥有对方所拥有的资源而且都在等待对方拥有的资源的情况就称为死锁。关于这个问题更详细的介绍请参考其他参考书。 

在MFC中对于各种同步对象都提供了相对应的类

在这些类中封装了上面介绍的对象创建,打开,控制,删除功能。但是如果要使用等待功能则需要使用另外两个类:CSingleLock和CMultiLock。这两个类中封装了WaitForSingleObject和WaitForMultipleObjects函数。如果大家觉的需要可以看看这些类的定义,我想通过上面的介绍可以很容易理解,但是在对象同步问题上我觉得使用API函数比使用MFC类更为直观和方便。

原文地址:https://www.cnblogs.com/lgz24/p/3605063.html