线程间同步方式

各个线程可以访问进程中的公共变量,资源,所以使用多线程的过程中需要注意的问题是如何防止两个或两个以上的线程同时访问同一个数据,以免破坏数据的完整性。数据之间的相互制约包括:

  1. 直接制约关系,即一个线程的处理结果,为另一个线程的输入,因此线程之间直接制约着,这种关系可以称之为同步关系。
  2. 间接制约关系,即两个线程需要访问同一资源,该资源在同一时刻只能被一个线程访问,这种关系称之为线程间对资源的互斥访问,某种意义上说互斥是一种制约关系更小的同步。

线程间的同步方式有四种

临界区

临界区对应着一个 CcriticalSection 对象,当线程需要访问保护数据时,调用 EnterCriticalSection 函数;当对保护数据的操作完成之后,调用 LeaveCriticalSection 函数释放对临界区对象的拥有权,以使另一个线程可以夺取临界区对象并访问受保护的数据。

PS: 关键段对象会记录拥有该对象的线程句柄即其具有 “线程所有权” 概念,即进入代码段的线程在 leave 之前,可以重复进入关键代码区域。所以关键段可以用于线程间的互斥,但不可以用于同步(同步需要在一个线程进入,在另一个线程 leave)

互斥量

互斥与临界区很相似,但是使用时相对复杂一些(互斥量为内核对象),不仅可以在同一应用程序的线程间实现同步,还可以在不同的进程间实现同步,从而实现资源的安全共享。

PS:

  1. 互斥量由于也有线程所有权的概念,故也只能进行线程间的资源互斥访问,不能由于线程同步;
  2. 由于互斥量是内核对象,因此其可以进行进程间通信,同时还具有一个很好的特性,就是在进程间通信时完美的解决了 “遗弃” 问题。

信号量

信号量的用法和互斥的用法很相似,不同的是它可以同一时刻允许多个线程访问同一个资源,PV 操作

PS: 事件可以完美解决线程间的同步问题,同时信号量也属于内核对象,可用于进程间的通信。

事件

事件分为手动置位事件和自动置位事件。事件 Event 内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。由 SetEvent() 来触发,由 ResetEvent() 来设成未触发。

PS: 事件是内核对象,可以解决线程间同步问题,因此也能解决互斥问题。