Windows线程同步与互斥技术 摘录一

1.1    线程同步概述

如果没有同步对象和操作系统对特殊事件监视的能力,线程可能被迫使用有副作用的技术使自己与特殊事件同步。不使用操作系统支持的线程同步技术,会产生许多问题,比如:分配不必要的CPU时间,浪费;在高低优先级线程间,若低线程负责信号重置任务,则可能永远无法执行重置。

 

1.2    临界区

1.2.1   概述

临界区:在所有同步对象中,临界区是最容易使用的,但它只能用于同步单个进程中的线程。临界区一次只允许一个线程取得对某个数据区的访问权。还有,在这些同步对象中,只有临界区不是内核对象,它不由操作系统的低级部件管理,而且不能使用句柄来操纵

 

1.         在进程中创建一个临界区,即在进程中分配一个CRITICAL_SECTION数据结构,该临界区结构的分配必须是全局的,这样该进程的不同线程就能访问它。

2.         在使用临界区同步线程之前,必须调用InitializeCriticalSection来初始化临界区。在释放资源之前,只需要初始化一次。

3.         VOID EnterCriticalSection:阻塞函数。The function returns when the calling thread is granted ownership。换言之,调用线程不能获取指定临界区的所有权时,该线程将睡眠,且在被唤醒之前,系统不会给它分配CPU

4.         执行临界区内的任务

5.         BOOL LeaveCriticalSection:非阻塞函数。将当前线程对指定临界区的引用计数减壹;在使用计数变为零时,另一等待此临界区的一个线程将被唤醒。

6.         当不需要再使用该临界区时,使用DeleteCriticalSection来释放临界区需要的资源。此函数执行后,再也不能使用EnterCriticalSectionLeaveCriticalSection,除非再次使用InitializeCriticalSection初始化了该临界区。

 

 

1.2.2   注意事项:

1.         临界区一次只允许一个线程访问,每个线程必须在视图操作临界区域数据之前调用该临界区域标志(即一个CRITICAL_SECTION全局变量)EnterCriticalSection后,其它想要获得访问权的线程都会置于睡眠状态,且在被唤醒以前,系统将停止为它们分配CPU时间片。换言之,临界区可以且仅可被一个线程拥有,当然,没有任何线程调用EnterCriticalSectionTryEnterCriticalSection时,临界区不属于任何一个线程。

2.         当拥有临界区所有权的线程调用LeaveCriticalSection放弃所有权时,系统只唤醒正等待中的一个线程,给它所有权,其它线程则继续睡眠。

3.         注意,拥有该临界区的线程,每一次针对此临界区的EnterCriticalSection调用都会成功(这里指的是重复调用也会立即返回),且会使得临界区标志(即一个CRITICAL_SECTION全局变量)的引用计数增壹。在另一个线程能够拥有该临界区之前,拥有它的线程必须调用LeaveCriticalSection足够多次,在引用计数降为零后,另一线程才有可能拥有该临界区。换言之,在一个正常使用临界区的线程中,calSectionLeaveCriticalSection应该成对使用。

4.         TryEnterCriticalSection
    BOOL TryEnterCriticalSection(
       LPCRITICAL_SECTION lpCriticalSection
   );
从函数声明便可看出端倪,EnterCriticalSection函数的返回值为VOID,而这里为BOOL。可见对于TryEnterCriticalSection的调用,需要我们判断其返回值。在调用TryEnterCriticalSection时,如果指定的临界区没有被任何线程(或还没有被任何调用线程)拥有,该函数将临界区的访问权给予调用的线程,并返回TRUE;不过,如果临界区已经被另一个线程拥有,它立刻返回FALSE值。TryEnterCriticalSectionEnterCriticalSection之间的最大区别在于TryEnterCriticalSection从来不挂起线程。

1.3    用内核对象同步线程

1.3.1   概述

临界区非常适合于序列化对一个进程中的数据的访问,因为它们的速度很快。但我们或许想要使一些应用程序与计算机中发生的其它特殊事件或者其它进程中执行的操作取得同步。这时临界区无能为力。就需要使用内核对象来同步。

 

下列内核对象可用来同步线程:

1.         进程,Processes

2.         线程,Threads

3.         文件,Files

4.         控制台输入,Console input

5.         文件变化通知,File change notifications

6.         互斥量,Mutexes

7.         信号量,Semaphores

8.         事件(自动重设事件和手动重设事件),Events

9.         可等的计时器(只用于Window NT4或更高),Waitable timers

10.     Jobs

 

每一个上面这些类型的对象都可以处于两种状态之一:有信号(signaled)和无信号(nonsignaled)。比如进程和线程在终结时其内核对象变为有信号,而在它们处于创建和正在运行时,其内核对象是无信号的。

 

1.3.2   内核对象同步应用

1.         某线程获得某进程的内核对象句柄时,它可以:改变进程优先级、获得进程的退出码;使本线程与某进程的终结取得同步等等。

2.         当获得某线程的内核对象句柄时,它可以:改变该线程运行状态、与该线程的终结取得同步等等。

3.         当获得文件句柄时,也可以:本线程可与某一个异步文件的I/O操作获得同步等等。

4.         控制台输入对象可用来使线程在有输入进入时被唤醒以执行相关任务等等。

5.         其它内核对象―――文件改变通知、互斥量、信号量、事件、可等计时器等―――都只是为了同步对象而存在。相应的,也有WIN32函数来创建、打开、关闭这些对象,将线程与这些对象同步。对这些对象,没有其它操作可以执行了。

 

1.3.3   互斥量独有的特性(另参附录的实验)

互斥量对象与所有其它内核对象的不同之处在于它是被线程所拥有的。其 它所有同步对象要么有信号,要么无信号,仅此而已。而互斥量对象除了记录当前信号状态外,还要记住此时那个线程拥有它。如果一个线程在得到一个互斥量对象 (即将其置为无信号态)后就终结了,互斥量也就废弃了。在这种情况了,互斥量将永远保持无信号态,因为没有其它线程能够通过调用ReleaseMutex来释放它。

系统发现产生这种情况时,就自动将互斥量设回有信号状态。其它等待该信号量的线程就会被唤醒,但函数的返回值为WAIT_ABANDONED而不是正常的WAIT_OBJECT_0。这时,其它线程可以知道互斥量是不是被正常释放。

其它的,互斥量与CRITICAL_SECTION类似。拥有该互斥量的线程,每次调用WaitForSingleObject都会立即成功返回,但互斥量的使用计数将增加,同样的,也要多次调用ReleaseMutex以使引用计数变为零,方可供别的线程使用。

原文地址:https://www.cnblogs.com/hummersofdie/p/2258349.html