信号量的基本同步模式

信令

一个线程向另一个线程发送信号,以通知它发生了某些事情。它完成了一个执行顺序的约束,解决了所谓的序列化问题。

集合点

考虑线程 A 的两个语句 A1,A2,线程 B 的两个语句 B1,B2。我们要求 A2 必须在 B1 后发生,B2 必须在 A1 后发生。换言之,A 必须等待 B,B 也必须等待 A,只有在它们都执行完步 1 后才能执行步 2。

因此,设计两个信号量 aX 表示线程 A 到达了集合点,bX 表示线程 B 到达了集合点。那么 A 发送 aX 并等待 bX 才继续执行,B 发送 bX 并等待 aX 才继续执行。

注意,发送一定要放在等待之前,否则可能会导致死锁。

互斥

考虑如何通过信号量来实现对一个变量的互斥访问。

为每个互斥变量安排一个信号量 m,初态为 1,所有访问这个变量的代码都应该套上 m.wait() 在前而 m.signal() 在后。

多路复用

类似互斥,但允许多个线程同时在临界区内执行,并强制给定一个并发线程数的上限。

为每个多路复用区安排一个信号量 m,初态为其数量限制,所有访问这临界区的代码都应该套上 m.wait() 在前而 m.signal() 在后。

屏障

我们从集合点模式出发做一些推广。现在有很多个线程,每个线程都执行两个步骤 rendezvous 和 critical,我们需要保证只要有任何一个线程未完成第一步,就不存在任何一个线程进入第二步。

这里我们需要两个部分,一个自带互斥锁的计数器 count 和一个标识屏障是否被推倒的信号量 bar

思路非常清晰,每个线程执行完 rendezvous 后修改计数器,如果计数器等于 n 则推倒屏障。如果只推倒一次是有问题的。两种解决方案:要么连续推倒 n 次,要么在每个线程等待屏障等到后再次推倒屏障。

rendezvous
m.wait()
	count++
m.signal()
if count==n: bar.signal()
bar.wait()
bar.signal()
critical

这里出现了一种新模式:快速连续的 wait 和 signal,我们将它称作旋转门。它一次只允许一个线程通过。它可以锁定以组织所有线程。

在本例中,初态下旋转门被锁定。第 n 个到达的线程将解锁它,然后 n 个线程将都可以通过。

可重用屏障

现在我们希望在所有线程都通过屏障后,再次锁定旋转门。

考虑到代码可能会循环执行,为了防止某些 HKJ 线程搞事情,我们设置两个旋转门 ts1,ts2。最初第一个被锁定,第二个被打开。当所有线程到达第一个时,我们锁定第二个并解锁第一个。对第二个也同理。

rendezvous
m.wait()
	cnt++
	if cnt==n: ts2.wait(), ts1.signal()
m.signal()
ts1.wait()
ts1.signal()
critical
m.wait()
	cnt--
	if cnt==0: ts1.wait(), ts2.signal()
m.signal()
ts2.wait()
ts2.signal()

另一个方案是连续推倒 n 次。

rendezvous
m.wait()
	cnt++
	if cnt==n: ts1.signal(n)
m.signal()
ts1.wait()
critical
m.wait()
	cnt--
	if cnt==0: ts2.signal(n)
m.signal()
ts2.wait()

我们以后可以将这种屏障模式描述为一个固定的类封装形式。

bar=Barrier(n)
bar.wait()

队列

这里说的队列是这样的:领袖和粉丝配对跳舞。当领袖到达时,会检查是否有粉丝在等待。如果有,他们就结伴然后干一些什么事,没有的话就抽根烟等一等。

我们需要两个信号量 lQfQ 来标识领袖等待的数量和粉丝等待的数量。这里似乎只是一个可以重叠的集合点,因此对领袖而言,我们有

fQ.signal()
lQ.wait()
dance()

独占队列

现在我们在队列问题的基础上,增加一个约束条件,每个领袖只能与一个粉丝同时调用 dance,换言之这个 dance 的调用必须由领袖和粉丝不断地交替进行。

对于领袖而言

m.wait()
if fCnt>0: fCnt--, fQue.signal()
else: lCnt++, m.signal(), lQue.wait()
dance()
rendezvous.wait()
m.signal()

对于粉丝而言

m.wait()
if lCnt>0: lCnt--, lQue.signal()
else: fCnt++, m.signal(), fQue.wait()
dance()
rendezvous.signal()

当一个领袖到达时,如果已经有等待的粉丝就唤醒他并开始跳舞,否则进入等待。最后的 rendezvous 是为了保证在本次配对结束前,不能有新的人打开互斥锁。

上一篇:Qt 构建一个私有的信号


下一篇:实验七、信号