线程的同步之事件对象
介绍:事件对象也属于内核对象
组成:
1.使用计数
2.布尔值:指明是自动重置还是人工重置
两种不同类型的事件对象:
人工重置:当人工重置的事件对象得到通知时,等待该事件的所有线程均变为可调度线程
自动重置:当一个自动重置的事件得到通知时,等待该事件的线程只有一个线程变为可调度线程
创建事件对象:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全性 BOOL bManualReset, // 是人工重置还自动重置 BOOL bInitialState, // 初始状态,为有信号还是没有 LPCTSTR lpName // 事件对象的名称 );
这里需要注意的是参数2,如果为设置为TRUE表示人工重置,此时你必须手动重置此事件对象为非信号状态
BOOL ResetEvent( HANDLE hEvent // handle to event );
如果参数2为FALSE,则等待线程被释放之后,系统自动重置事件对象为非信号状态(自动重置)
设置事件对象为有信号状态的方法:
1.在创建的时候指定第三个参数为TRUE
2.在创建的时候第三个参数为FALSE(非信号状态)
然后调用一个函数:
BOOL SetEvent( HANDLE hEvent // handle to event );
将事件对象设置为有信号状态
注意:在重点说明一下对于一个人工重置的事件对象,需要注意一点的是:当人工重置的事件对象为有信号状态时,等待该对象的所有线程均变为可调度状态
对于一个自动重置的事件对象来说,需要注意一点是:当自动重置的事件对象变为有信号状态时,等待该对象的线程只有一个变为可调度状态
命名的事件对象:
对于命名的事件对象A被创建后,如果你在其他线程中再次创建一个同名的事件对象时
调用GetLastError()后会返回ERROR_ALREADY_EXISTS
利用此功能可以实现同一应用程序只能有一个实例运行
线程的同步之临界区对象
打个比方,这里把临界区对象比作公用电话亭,
一个人进入公用电话亭就好比获得了临界区对象,此时其它人就不能再使用这个电话亭,直到你使用完为止(对应于线程中就是一旦某线程获得了临界区对象,其他线程就不能访问此临界区了直到此临界区对象被释放为止)
建立并初始化临界区对象
VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section );
获得临界区对象
VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section );
释放临界区对象
VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section );
释放临界区对象的所有资源
VOID DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section );
使用临界区对象可能会造成死锁问题:
线程的死锁:
线程1拥有了临界区对象A,等待临界区对象B,线程2拥有临界区对象B等待临界区对象A
互斥对象、事件对象和关键代码段的比较
互斥对象和事件对象属于内核对象,利用内核对象进行线程的同步,速度较慢,但是利用内核对象可以在多个进程的各个线程间进行同步
关键代码段是工作在用户模式下,同步速度较快,但是容易造成死锁