Windwos中的线程同步
2019年5月23日
9:57
Windows操作系统的运行方式(程序运行方式)为"双模式操作"(Dual-mode Operation)。
- 用户模式(User mode):运行应用程序的基本模式,禁止访问物理设备,而且会限制访问的内存区域
- 内核模式(Kernal mode):操作系统运行时的模式,不会限制访问的内存区域,同时访问的硬件设备不会受限
在应用程序的运行中,Windows操作系统不会一直停留在用户模式,而是在用户模式和内核模式之间切换。例如创建线程的函数虽然时在应用程序中调用,但是实际上却需要切换到内核模式来由操作系统完成创建线程的操作。
定义这两种模式主要是为了提高安全性,应用程序的运行时报错会破坏操作系统以及各种资源。然而如果定义了这两种模式的话,应用程序的错误就很难影响到操作系统。
当然模式切换对于操作系统而言其实也是一种负担,频繁的模式切换会影响性能。
用户模式下的同步
用户模式的同步时用户模式下进行的同步,无需借助操作系统的帮助。也就是该同步方式无需切换到内核模式。他的最大优点是速度快。但因为无需借助操作系统,所以在其功能上存在由一定的局限性。
内核模式同步
内核模式同步的优点有:
- 比用户模式提供的功能更多
- 可以指定超时,防止产生死锁
提供的更多的功能包括进行不同进程之间的同步,因为是基于内核对象的,而不属于某一进程而属于操作系统拥有并管理。
基于CRITICAL_SECTION的同步(用户模式)
借用Linux下的互斥量来理解CRITICA_SECTION并无不可(我的理解就是由进程管理的互斥量),使用也是差不多的
CRITICAL_SECTION的创建与删除
#include <windows.h>
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
lpCriticalSection: CRITICAL_SECTION对象的地址值
CRITICAL_SECTION的开锁与解锁
#include <windows.h>
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
内核模式的同步方法
基于互斥量(Mutual Exclusion)对象的同步
创建互斥量对象
#include <windows.h>
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner, LPCTSTR lpName);
->>成功返回互斥量句柄,失败返回NULL
lpMutexAttributes: 安全配置信息,默认配置则传递NULL
bLnitialOwner:为TRUE,则互斥量对象进入non-signaled状态;为FALSE,则互斥量对象进入signaled状态
lpName:用于命名互斥量对象,NULL则无名
销毁内核对象
#include <windows.h>
BOOL CLOSEHandle(HANDLE hObject);
->>成功时返回TRUE,失败时返回FALSE
销毁内核对象,不只限于互斥量
释放互斥量
#include <windows.h>
BOOL ReleaseMutex(HANDLE hMutex);
->>成功时返回TRUE,失败时返回FALSE
如果按照Windows的命名来理解的话,我们可以想象只有当我们捕捉到Mutex时后面的代码才可以执行,一开始Mutex是可以被捕捉的(当他为signaled状态时),而被捕捉以后就变成了non-signaled状态,那么其他的就要等待Mutex变为可被捕捉的,就需要捉到Mutex的线程将Mutex释放,也就是将Mutex置为signaled状态
WaitForSingleObject(hMutex, INFINITE);
// 临界区开始
// …
// 临界区结束
ReleaseMutex(hMutex);
基于信号量对象的同步(内核对象)
与Linux下的信号量同步相同,也是利用"信号量值(Semaphore Value)"的整数值来完成同步,并且该值都不能小于0。当信号量值大于0时为signaled状态,等于0时为non-signaled状态
创建信号量对象
#include <windows.h>
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);
->> 成功时返回创建的信号量对象的句柄,失败返回NULL
lpSemapthoreAttributes: 安全配置信息,默认配置传NULL
lLnitialCount:指定信号初始值
lMaximumCount:指定信号量的最大值
lpName:命名信号量对象,传NULL,无名
释放信号量对象
#include <windows.h>
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount,
LPLONG lpPreviousCount);
->>成功时返回TRUE,失败时返回FALSE
hSemaphore:信号量对象句柄
lReleaseCount:信号量值增加的指定值,若增加后超过最大值则不增加
lpPreviousCount:保存修改之前值的变量的地址,不需要则传NULL
WaitforSingleObject(hSemaphore, INFINITE);
// 临界区的开始
// 。。。
// 临界区去的结束
ReleaseSemaphore(hSemaphore, 1, NULL);
Mutex(互斥量)和Semaphore(信号量)都是"auto-reset"内核对象,也即是说在调用WaitForSingleObject函数以后会由signaled状态回到non-signaled状态。
当然Semaphore有所不同,他在调用WaitForSingledObject函数以后会将signaled状态的信号量值-1,而调用Release函数会将信号量值+N(N为指定的值).
基于事件对象的同步(内核对象)
与互斥量和信号量不同,在该方式下创建对象时,可以选择以auto-reset模式运行的内核对象,或者时以manual-reset模式运行的内核对象。事件对象的主要特点就是可以创建以manual-reset模式的对象。
创建事件对象
#include <windows.h>
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttrobutes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);
->>成功返回事件对象句柄,失败返回NULL
lpEventAttributes:安全配置参数,默认配置NULL
bManualReset: TRUE为manual-reset模式的事件对象,FALSE为auto-reset模式的事件对象
bInitialState:TRUE为signaled状态的事件对象,FALSE为non-signaled状态
lpName:命名事件对象,无名,NULL
更改事件对象状态
#include <windows.h>
BOOL ResetEvent(HANDLE hEvent); // to the non-signaled
BOOL SetEvnet(HANDLE hEvent); // to the signaled