Windows系统编程C/C++--互斥量
Posted 水澹澹兮生烟.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Windows系统编程C/C++--互斥量相关的知识,希望对你有一定的参考价值。
目录
在用户模式下进行线程同步的最大的好处就是速度快。如果关心应用程序性能问题,那么应该先考虑用户模式下的同步机制是否适用。但是同时也存在局限性,对于一些函数只能对一个值进行操作,他们从来不会将线程切换到等待状态。因此我们可以用关键段来把线程切换到等待状态,但是只能对待同一个进程中的线程进行同步。除此之外还容易陷入死锁状态,只是因为我们无法进入关键段指定一个最长时间。
对于线程同步来说,这些内核对象中的每一种要么处于触发状态,要么处于未触发状态。在进程内核对象的内部有一个布尔变量,当系统创建内核对象的时候会把这个变量的初始值设置为FALSE(未触发);当进程终止时,操作系统会自动将内核对象中的这些布尔值设置为TURUE,标识该对象已经触发。Windows提供帮助进程线程同步的内核对象:事件,可等待计时器,信号量以及互斥量。
1.互斥量内核对象
它用来确保一个线程独占一个资源的访问。互斥量内核对象包含一个使用计数,线程ID以及一个递归计数。互斥量与关键段的行为完全相同,不同之处是关键段时用火模式下的同步对象,而互斥量是内核对象。这就意味着不同进程中的线程可以访问一个互斥量,且在等待对资源的访问全是指定一个最长等待时间。
线程ID用来标识当前占用这个互斥量的是系统中的哪个线程,递归计数器表示这个线程占用该互斥量的次数。
下面先说明一下创建互斥量的函数:
- CreateMutex()
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL bInitialOwner,
PCTSTR pszName
);
psa:指向对象的指针
bInitialOwer:初始互斥化对象的所有者
pszname:指向互斥对象的指针
- CreateMutexEx()
我们也可以直接使用CreateMutexEx()在dwDesiredAccess参数中指定访问权限。参数dwFlags代替了CreateMutex()函数中参数bInitialOwned:0表示FALSE,CREATE_MUTEX_INITIAL_OWNER等价与TRUE。
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
PCTSTR pszName
DWORD dwFlags,
DWORD dwDesiredAccess
);
- OpenMutex()
我们也可以调用OpenMutex()函数来得到一个一个已经存在的互斥量的句柄,该句柄与当前进程相关。
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName
);
bInitialOwner是用来控制互斥量的初始状态,如果传的时FALSE(通常情况),则互斥量对象的线程ID和递归计数器都将被设置成0.这就意味这互斥量不为任何线程占用,因此处于触发状态。如果给他传TRUE,那么对象的线程ID将被设置为调用线程的线程ID,递归技术将被设置成1,由于线程ID为非零值,因此互斥量最初处于未触发状态。
为了获得被保护资源的访问权限,线程要调用一个等待函数并出入互斥量的句柄。在内部等待函数会检查线程ID是否为0(此时,互斥量处于触发状态)。如果0,则函数会把线程ID设为调用线程ID,把递归计数设置为1,然后让调用线程继续运行。
如果等待函数检测到线程ID不为0(互斥量处于未触发状态),那么调用线程将进入等待状态。当另一个线程将互斥量的线程的线程ID设置为0的时候,系统会记得有一个线程正在等待,于是会把线程ID设置为正在等待的ID,把递归计数设置为1,如果为0,那么函数会把线程ID设置为正在等待的那个线程ID,把递归技术设置为1,使用正在等待的线程变成可调度状态。这些对互斥量内核对象的检查和修改都是以原子方式进行的。
注意:在用来出发普通内核对象和撤销触发普通内核对象的规则中,有一条不适用于互斥量,要注意。假设线程试图登海一个未触发的互斥量对象,在这种情况下,线程通常会进入等待状态。但是系统会检查想要获得互斥量的线程ID与互斥量对象内部记录的线程ID是否相同。如果线程ID一致,则系统会让线程保持可调度状态,即是该互斥量尚未触发。此时是与其他不同的,每次线程成功等待了一个互斥量,互斥量对象的递归计数会递增,使递归计数大于1的唯一途径是利用这个例外,让线程多次等待同一个互斥量。
当成功的等到了互斥量,线程就知道自己已经独占了对受保护资源的访问。任何试图(通过等待互斥量)后去对资源的访问权限的线程将进入等待状态。当目前占有访问权限的线程不再需要访问资源的时候,它必须调用RealseMutex函数来进行释放互斥量,这个函数会将对象的递归计数减一,如果线程成功等待了互斥量互斥量对象不止一次那么线程必须调用ReleaseMutex相同的次数才能使用对象的递归计数变成0.当地贵计数变成0的时候,函数还会将线程ID设为0,这样就触发了对象。
当对象被触发的时候,系统会自动检查有没有其他线程正在等待该互斥量,如果有,那么系统会选择一个正在等待的线程,把互斥量的所有权给他。这就意味着把对象内部的线程ID设为所有选择的那个线程ID,并把递归计数器设置为1;如果没有线程在等待会吃凉,那么互斥量会保持在触发状态,这样下一个等待他的线程就可以立即得到它。
2.遗弃问题
互斥量它具有线程所有权,他即使在未触发状态下也能为线程所获取,它不仅仅适用于试图获取互斥量的线程,也适用于试图释放互斥量的线程。
使用RealseMutex函数进程释放,函数会检查调用线程的线程ID与互斥量内部保存的线程ID是否一致。如果一致,则递归计数会递减。如果不一致,则函数将不执行任何操作并返回FALSE,这是调用GetLastError()会返回ERROR_NOT_OWNER。
此时如果占用互斥量的线程在释放互斥量之前终止,那么对互斥量和正在等待该互斥量的线程来说,系统会认为互斥量被遗弃,由于占用他的线程提前终止,无法进行释放此互斥量。系统会记录所有互斥量和线程内核对象,因此他知道互斥量被遗弃后,会将互斥量对象的线程ID设置为0,将他的递归计数设置为0,然后系统会检查有没有其他线程正在等待该互斥量,如果有,把对象内部的线程ID设置为现在线程的ID,并将递归计数设置为1,这样备选的线程就变成可调度状态。唯一不同的是等待函数不在返回通常的WAIT_OBJECT_0,而是返回一个特殊的值WAIT_ABANDONED。这个返回值表示线程正在等待的互斥量为其他线程所占用,但线程完成对共享资源的使用之前终止了。刚获得互斥量的线程并不知道资源现在处于什么状态, 他可能已经被完全损坏了。
以上是关于Windows系统编程C/C++--互斥量的主要内容,如果未能解决你的问题,请参考以下文章