5、线程同步
加载套接字库AfxSocketInit
一、相关概念
线程同步的方法,除了上节介绍的互斥对象外,还有事件对象和关键代码段。
1、事件对象
时间对象也属于内核对象,包含:一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是为通知状态的布尔值有两种不同类型的事件对象。
一种是人工重置的事件,另一种是自动重置的事件,当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程,同时操作系统会将该事件对象设置为无信号状态;当代码完成后,要调用setEvent来将事件对象设置为有信号状态。人工重置的事件对象,在一个线程得到该事件对象之后,需要自己通过ResetEvent来显式的将其设置为无信号状态。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
BOOL bManualReset, // reset type
BOOL bInitialState, // initial state
LPCTSTR lpName // object name
);
WaitForSingleObject(g_hEvent,INFINITE); //等待事件,如果事件可用,运行下面的代码,并且将事件状态设置为不可用状态,如果事件不可用,一直等待。
SetEvent(g_hEvent) //将事件设置为可用的状态
ResetEvent(g_hEvent) //将事件设置为不可用状态
一般情况下WaitForSingleObject和SetEvent配对使用。
所以,为了实现线程的同步,不应该使用人工重置的事件对象,而应使用自动重置的事件对象。
2、关键代码段
1)关键代码段(临界区)工作在用户方式下。
2)关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某资源的访问权。
一般要经历以下四步:
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // critical section
);
VOID DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // pointer to critical section object
);
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // pointer to critical section object
);
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // address of critical section object
);
在调用LeaveCriticalSection函数时,该函数会立即返回,并不导致线程等待,所以释放临界区对象所有权的顺序是无所谓的。
4、互斥对象,事件对象,与关键代码段的比较
互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内河对象,可以在多个进程中的各个线程间进行同步。
关键代码段时工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。
在编写多线程程序,并需要实现线程同步时,首选关键代码段。
如果需要在多个进程间的各个线程间实现同步的话,可以使用互斥对象和事件对象。
二、相关程序
略。