线程间的同步

线程间的同步
之前讲到通过互斥对象进行线程间的同步,下面介绍其他的几种方式来保持线程之间的同步。

一、事件对象

事件对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。

有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。

创建事件对象的函数为CreateEvent,这个函数的功能是创建或者打开一个命名或者未命名的事件对象。
函数原型:
HANDLE WINAPI CreateEvent(
  __in          LPSECURITY_ATTRIBUTES lpEventAttributes,
  __in          BOOL bManualReset,
  __in          BOOL bInitialState,
  __in          LPCTSTR lpName
);
参数说明:
第一个参数是一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。在这个地方我们可以给它传递NULL,让它使用默认的安全性。
第二个参数指定是人工重置还是自动重置的事件对象被创建,如果是TRUE,表示人工重置的事件对象被创建,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。
第三个参数指定事件对象的初始状态,如果为TRUE,初始状态为有信号状态;否则为无信号状态。
第四个参数是给事件对象取名字。

ResetEvent函数的功能是是把指定的事件对象设置为无信号状态。
函数原型:
BOOL WINAPI ResetEvent(
  __in          HANDLE hEvent
);
参数说明:参数为事件对象的句柄。
返回值:若函数调用成功,返回值为非0,不成功则返回值为0。

SetEvent函数的功能是是把指定的事件对象设置为无信号状态。
函数原型:
BOOL WINAPI SetEvent(
  __in          HANDLE hEvent
);

参数说明:参数为事件对象的句柄。
返回值:若函数调用成功,返回值为非0,不成功则返回值为0。

注意:在使用事件对象作线程间同步的时候,对于人工重置的事件对象,当它变为有信号状态的时候,所有的线程都可以运行。对于自动重置的事件对象,当它变为有信号状态的时候,所有等待该事件对象的线程只能有一个变为可调度线程,同时操作系统会将这个事件对象设置为非信号状态。而人工重置的事件对象,在一个线程得到该事件对象之后呢并不会将它设置为非信号状态。

通过创建一个命名的事件对象,可以使一个应用程序只有一个实例运行。这个与创建一个命名的互斥对象类似。

二、关键代码段(临界区)

关键代码段(临界区)工作在用户方式下。
关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。
EnterCriticalSection函数
函数原型:
void WINAPI EnterCriticalSection(
  __in_out      LPCRITICAL_SECTION lpCriticalSection
);
函数功能:等待指定临界区对象的所有权,当被调用线程被赋予所有权的时候,函数返回。若被调用线程没有赋予所有权的时候,那此函数一直等待,从而导致线程等待。
参数说明:指向临界区对象的指针。
InitializeCriticalSection函数
函数原型:
void WINAPI InitializeCriticalSection(
  __out         LPCRITICAL_SECTION lpCriticalSection
);
函数功能:初始化一个临界区对象。
LeaveCriticalSection函数
函数原型:
void WINAPI LeaveCriticalSection(
  __in_out      LPCRITICAL_SECTION lpCriticalSection
);
函数功能:释放指定临界区对象的所有权。
DeleteCriticalSection函数
函数原型:
void WINAPI DeleteCriticalSection(
  __in_out      LPCRITICAL_SECTION lpCriticalSection
);
函数功能:释放一个没有被拥有的临界区对象的所有的相关资源。

通过调用上面这四个函数可以利用临界区对象实现线程之间的同步。

线程死锁

线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的拥有权,就造成了死锁。

互斥对象、事件对象与关键代码段的比较

互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章