1.3.4 同步机制

在有的情况下,线程之间的运行是相互影响的,比如对共享资源的访问就需要访问共享资源的线程相互同步,以免破坏共享资源的连续性。操作系统可实现下列线程同步机制,用来完成线程的同步和共享资源的互斥访问。

1.事件(Event)

事件对象是一个最基础的同步对象,事件对象一般处于两种状态:空闲状态和占用状态。一个内核线程可以等待一个事件对象,如果一个事件对象处于空闲状态,那么任何等待该事件对象的线程都不会阻塞。相反,如果一个事件对象处于占用状态,那么任何等待该对象的线程都进入阻塞状态(Blocked)。

一旦事件对象的状态由占用变为空闲,那么所有等待该事件对象的线程都被激活(状态由Blocked改变为Ready,并被插入Ready队列),这一点与下面讲述的互斥体不同。

2.信号量(Semaphore)

信号量也是最基础的同步对象之一,一般情况下,信号量维护一个计数器,假设为N,每当一个内核线程调用WaitForThisObject等待该信号量对象时,N就减1,如果N小于零,那么等待的线程将被阻塞,否则继续执行。

3.互斥体(Mutex)

互斥体是一个二元信号量,即 N 的值为1,这样最多只有一个内核线程占有该互斥体对象,当这个占有该互斥体对象的线程释放该对象时,只能唤醒另外一个内核线程,其他的内核线程将继续等待。

注意互斥体对象与Event对象的不同,在Event对象中,当一个占有Event对象的线程释放该对象时,所有等待该Event对象的线程都将被激活,而Mutex对象则只有一个内核线程被激活。

4.内核线程对象(KernelThreadObject)

内核线程对象本身也是一个互斥对象,即其他内核线程可以等待该对象,从而实现线程执行的同步。但与普通的互斥对象不同的是,内核线程对象只有当状态是Terminal时才是空闲状态,即如果任何一个线程等待一个状态是非Terminal的内核线程对象,那么将会一直阻塞,直到等待的线程运行结束(状态修改为Terminal)。

5.睡眠

一个运行的线程可以调用Sleep函数而进入睡眠状态(Sleeping),进入睡眠状态的线程将被加入Sleeping队列。

当睡眠时间(由Sleep函数指定)到达时,系统将唤醒该睡眠线程(修改状态为Ready,并插入Ready队列)。

6.定时器

另外一个内核线程同步对象是定时器。定时器是操作系统提供的最基础服务之一,比如线程可以调用SetTimer函数设置一个定时器,当设置的定时器到时后,系统会给设置定时器的线程发送一个消息。与Sleep函数不同的是,内核线程调用Sleep函数后将进入阻塞状态,而调用SetTimer函数之后,线程将继续运行。