一、引言
线程同步类似线程锁,主要用来安全地访问临界资源。Windows 下有两种线程同步的方式,一种是用户模式下的线程同步,一种是内核模式下的线程同步。
二、用户模式
用户模式下的线程同步由于不需要切换内核模式所以性能相对较高,但是功能没有内核模式强大。
用户模式下的线程同步主要使用 CRITICAL_SECTION
对象。
函数原型:
1
2
3
4
5
6
7
#include <windows.h>
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void LeaveXriticalSection(LPCRITICAL_SECTION lpCriticalSection);
上面的所有函数都需要传入 CRITICAL_SECTION
对象的地址值。
代码:
三、内核模式
内核模式下的线程同步有多种实现方法。
基于互斥量对象的同步
创建互斥量对象:
1
2
3
#include <windows.h>
HANDEL CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);
lpMutexAttributes
:安全相关的配置信息,传递 NULL 使用模式安全配置
bInitialOwner
:传入 TRUE 则创建出的互斥量对象处于调用该函数的线程,并且进入 non-signaled 状态。传入 FALSE 则创建出的互斥量对象不属于任何线程,并设置为 signaled 状态。
lpName
:用于命名互斥量对象,传入 NULL 则创建无名的互斥量对象。
返回值:成功返回互斥量对象句柄,失败返回 NULL。
销毁互斥量对象:
1
2
3
#include <windows.h>
BOOL CloseHandle(HANDLE hMutex);
hMutex
:传入需要销毁的互斥量对象句柄
获取互斥量对象:
1
2
3
#include <windows.h>
DWORD WaitForSingleObjext(HANDLE hMutex, DWORD dwMilliseconds);
hMutex
:互斥量对象句柄
dwMilliseconds
:阻塞 dwMilliseconds
毫秒,传递 INFINITE
则一直阻塞。
返回值:signaled
返回 WAIT_OBJECT_0
,超时返回 WAIT_TIMEOUT
。
释放互斥量对象:
1
2
3
#include <windows.h>
BOOL ReleaseMutex(HANDLE hMutex);
hMutex
:传入需要释放的互斥量对象句柄
当一个线程获取互斥量对象时,只有当互斥量处于 signaled
状态时才可以被获取,被获取之后该互斥量对象会进入 non-signaled
状态,当该互斥量被释放时又会转换回 signaled
状态。
代码:
基于信号量对象的同步
Windows 中基于信号量对象的同步也与 Linux 下的信号量类似。
创建信号量对象:
1
2
3
#include <windows.h>
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpNmae);
lpSemaphoreAttributes
:安全配置信息,传入 NULL 使用默认安全配置
lInitialCount
:信号量的初始值,应大于 0 小于 lMaximumCount
lMaximumCount
:信号量的最大值,该值为 1 时类似互斥量
lpNmae
:用于命名信号量对象,传入 NULL 时创建无名的信号量对象。
获取信号量与获取互斥量方法一样。
释放信号量对象:
1
2
3
#include <windows.h>
HANDLE ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);
hSemaphore
:需要释放的信号量对象句柄
lReleaseCount
:可以指定信号量增加的值,若超过最大值则不增加且返回 FALSE。
lpPreviousCount
:需要传入一个变量地址,用于保存之前的值,不需要时传递 NULL。
类似于互斥量,当一个线程获取信号量对象时,只有当信号量处于 signaled
状态时才可以被获取,被获取之后该信号量对象信号量值会减 1,当值为 0 时进入 non-signaled
状态,当该信号量被释放时信号量值增加 1 然后会转换回 signaled
状态。
代码:
基于事件对象的同步
事件同步对象与前两种同步方法相比有很大不同,区别在于该方式下创建对象时,可以在事件触发时自动切换 non-signaled
状态的 auto-reset
模式(类似于互斥量,但是互斥量有所有权,事件对象没有所有权)和需要手动切换 non-signaled
状态的 manual-reset
模式。
创建事件对象:
1
2
3
#include <windows.h>
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);
lpEventAttributes
:安全配置信息,传入 NULL 使用默认安全配置
bManualReset
:传入 TRUE 创建 manual-reset 模式,传入 FALSE 创建 auto-reset 模式
bInitialState
:传入 TRUE 创建 signaled 状态,传入 FALSE 创建 non-signaled 状态
lpName
:用于命名信号量对象,传入 NULL 时创建无名的信号量对象。
修改状态:
1
2
3
4
#include <windows.h>
BOOL ResetEvent(HANDLE hEvent); // to the non-signaled
BOOL SetEvent(HANDLE hEvent); // to the signaled
代码: