线程同步的几种方法
一 : 使用内核对象
1:事件对象
事件对象分为两种
当一个手动重置事件对象被触发时 正在等待该事件的素有线程都将变成可调度的状态( 一旦有信号被触发 则 所有等待线程都将往下执行)
而当一个自动重置事件对象被触发的时候,只有一个证在等待该事件的线程会变成可调度状态(一旦有信号被触发,只有一个等待线程会往下执行 再有信号被触发时 会再有另外一个线程往下执行)
当WaitForSingleObject等待到一个手动重置事件对象信号的之后 要希望事件对象再变为无信号状态 需要调用ResetEvent
而当等待到一个自动重置事件对象的之后 会自动将事件对象设为无信号状态 无需调用ResetEvent
Event Function | Description |
Creates or opens a named or unnamed event object. | |
Opens an existing named event object. | |
Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads. | |
Sets the specified event object to the nonsignaled state. | |
Sets the specified event object to the signaled state. |
2:互斥量对象
互斥量对象用来确保一个线程独占对一个资源的访问
CreateMutex创建一互斥量对象
OpenMutex 打开一互斥量对象
ReleaseMutex释放互斥量对象所有权
A线程创建hMutex之后
B线程
WaitForSingleObject(hMutex, 5000L); à一旦等待到互斥量信号便拥有了对以下资源的访问权 其他线程就进不来了
//…Code
ReleaseMutex(hMutex) à释放互斥量对象所有权 让其他线程可以进来
Mutex Function | Description |
Creates or opens a named or unnamed mutex object. | |
Opens an existing named mutex object. | |
Releases ownership of the specified mutex object. |
3:临界区
临界区:简述
如果您非常熟悉临界区,并可以不假思索地进行应用,那就可以略过本节。否则,请向下阅读,以对这些内容进行快速回顾。如果您不熟悉这些基础内容,则本节之后的内容就没有太大意义。
临界区是一种轻量级机制,在某一时间内只允许一个线程执行某个给定代码段。通常在修改全局数据(如集合类)时会使用临界区。事件、多用户终端执行程序和信号量也用于多线程同步,但临界区与它们不同,它并不总是执行向内核模式的控制转换,这一转换成本昂贵。稍后将会看到,要获得一个未占用临界区,事实上只需要对内存做出很少的修改,其速度非常快。只有在尝试获得已占用临界区时,它才会跳至内核模式。这一轻量级特性的缺点在于临界区只能用于对同一进程内的线程进行同步。
临界区由 WINNT.H 中所定义的 RTL_CRITICAL_SECTION 结构表示。因为您的 C++ 代码通常声明一个 CRITICAL_SECTION 类型的变量,所以您可能对此并不了解。研究 WINBASE.H 后您会发现:
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
我们将在短时间内揭示 RTL_CRITICAL_SECTION 结构的实质。此时,重要问题在于 CRITICAL_SECTION(也称作 RTL_CRITICAL_SECTION)只是一个拥有易访问字段的结构,这些字段可以由 KERNEL32 API 操作。
在将临界区传递给 InitializeCriticalSection 时(或者更准确地说,是在传递其地址时),临界区即开始存在。初始化之后,代码即将临界区传递给 EnterCriticalSection 和 LeaveCriticalSection API。一个线程自 EnterCriticalSection 中返回后,所有其他调用 EnterCriticalSection 的线程都将被阻止,直到第一个线程调用 LeaveCriticalSection 为止。最后,当不再需要该临界区时,一种良好的编码习惯是将其传递给 DeleteCriticalSection。
在临界区未被使用的理想情况中,对 EnterCriticalSection 的调用非常快速,因为它只是读取和修改用户模式内存中的内存位置。否则(在后文将会遇到一种例外情况),阻止于临界区的线程有效地完成这一工作,而不需要消耗额外的 CPU 周期。所阻止的线程以内核模式等待,在该临界区的所有者将其释放之前,不能对这些线程进行调度。如果有多个线程被阻止于一个临界区中,当另一线程释放该临界区时,只有一个线程获得该临界区。
Critical-Section Function | Description |
Releases all resources used by an unowned critical section object. | |
Waits for ownership of the specified critical section object. | |
Initializes a critical section object. | |
Initializes a critical section object and sets the spin count for the critical section. | |
Releases ownership of the specified critical section object. | |
Sets the spin count for the specified critical section. | |
Attempts to enter a critical section without blocking. |
以上 临界区的优点是速度快 (只是读取和修改用户模式内存中的内存位置)
事件对象和互斥量要去内核修改信号(在Ring3及Ring0之间的转换相当消耗时间) 但是缺点是会造成死锁