在代码中使用二进制信号量
Posted
技术标签:
【中文标题】在代码中使用二进制信号量【英文标题】:Use of binary semaphore in the code 【发布时间】:2018-04-17 22:33:27 【问题描述】:某个计算生成两个数组a
和b
,使得a[i]=f(i) for 0 ≤ i < n and b[i] = g(a[i]) for 0 ≤ i < n
。假设这个计算被分解成两个并发进程X
和Y
,这样X
计算数组a
和Y
计算数组b
。这些进程使用两个二进制信号量R
和S
,都初始化为零。数组a
由两个进程共享。进程的结构如下所示。
Process X:
private i;
for (i=0; i < n; i++)
a[i] = f(i);
ExitX(R, S);
Process Y:
private i;
for (i=0; i < n; i++)
EntryY(R, S);
b[i]=g(a[i]);
以下哪一项代表ExitX
和EntryY
的正确实现?
(A)
ExitX(R, S)
P(R);
V(S);
EntryY (R, S)
P(S);
V(R);
(B)
ExitX(R, S)
V(R);
V(S);
EntryY(R, S)
P(R);
P(S);
(C)
ExitX(R, S)
P(S);
V(R);
EntryY(R, S)
V(S);
P(R);
(D)
ExitX(R, S)
V(R);
P(S);
EntryY(R, S)
V(S);
P(R);
我认为答案应该是 (B),因为进程 Y
中的关键部分在 X
中的关键部分被执行之前不应执行(a[i]
首先被填充,这将必须用于b[i]
),所以在执行X
之后,根据Y
中临界区入口处的选项(B)我们会找到R=1
,S=1
,所以现在临界区在Y
可以执行。
问题:正确答案是(C),我哪里错了?
【问题讨论】:
B 折叠了两把锁,所以它不可能是正确的。这里的重点是协调两个循环。让我写下来。 【参考方案1】:'B' 如果它们不是二进制信号量,则可以工作:在这种情况下,X 可以创建一个元素,增加一个信号量,而 Y 可以等待该信号量并使用这些项目。信号量可以计算有多少项目可供处理。一个信号量就足够了。
但是你有二进制信号量。所以你最多只能数一个,例如X 可以创建一个元素,向信号量发出信号,但随后不能继续创建元素,因为它不能将该信号量值提高到“2”(或更多)。所以它必须等待那个单个元素被 Y 识别。并且这个等待引入了第二个信号量,当当前元素被处理时向 X 发出信号。重要的是要记住,P 会在必要时等待信号量增加(而 V 会增加),因此 X 不能等待单个信号量回到 0,因为没有这样的操作。
这就是“C”正在做的事情,S 实际上是“数据准备就绪”信号,R 是“确认”。 X 说,它准备好了,然后等待确认。 Y 等待就绪并发送确认。
【讨论】:
【参考方案2】:首先,考虑一下为什么我们在这里甚至需要两个信号量。原因是我们这里有两件事要协调,
-
在 X 循环完成之前,Y 循环无法启动
i
i
X 循环无法在 Y 完成 i
之前启动 i+1
。
所以有两个信号量,每个信号量在上面管理一个点。
信号量达到 1 需要从 ExitX
调用 P
。而EntryY
需要拨打V
。所以B已经不在了。要实现 2,我们需要 V
in ExitX
和 P
in EntryY
。
所以看看A,没有人增加任何东西,所以这是一个死锁。
C 完成这项工作。
D 不太正确,因为 X
和 Y
可能会在调用该信号量的任何 P
之前两次命中 V
。
【讨论】:
这里的函数 ExitX 和 EntryY 被认为是原子的,对吧? @chunky 不,不是。只有P
和 V
是原子的。
这里ExitX
执行的那一刻,信号量的更新值为S=0 , R=1
,所以现在根据option (C)
中的EntryY
控制到达并执行,process Y
中的b[i]=g(a[i])
,现在更新的信号量值为S=1 , R=0
,这确保a[i] = f(i)
在process X
中再次运行,对吧?
@chun 没有。 ExitX 会阻塞,因为 S 最初是 0。所以 EntryY 需要先走。
但同样R
也是0
,那么EntryY
怎么能先走呢?以上是关于在代码中使用二进制信号量的主要内容,如果未能解决你的问题,请参考以下文章