假设您perl
有semop
朋友,您可以使用 System V 信号量在孩子之间进行同步。请参阅下面的工作示例程序。
我们从通常的正面问题开始。代码使用内置的IPC::SysV和
IPC::Semaphore模块,而不是直接调用低级信号量操作
。
#! /usr/bin/env perl
use strict;
use warnings;
use IPC::Semaphore;
use IPC::SysV qw/ IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT /;
该程序将子进程分为两个阶段。第一阶段的子进程运行到完成,在没有同步问题的情况下执行他们的处理。我们可以拥有任意数量的这些。
我们有一个第二阶段进程,但它在所有
第一阶段子进程完成后执行。
下面是简单的占位符实现。
# how many other children the last child must wait for
my $FIRST_STAGE_CHILDREN = 2;
sub first_stage {
my($id) = @_;
print "[$$] hello from child $id\n";
sleep rand 10;
print "[$$] child $id done\n";
}
sub second_stage {
print "[$$] hello from second-stage child!\n";
}
为了实现第一阶段和第二阶段之间的同步,程序创建了一组信号量,其大小等于第一阶段子节点的数量。当第一阶段的孩子完成时,程序会释放对应于该孩子的特定信号量。
my $sem = IPC::Semaphore->new(
IPC_PRIVATE, $FIRST_STAGE_CHILDREN,
S_IRUSR | S_IWUSR | IPC_CREAT)
or die "$0: failed to create semaphore: $!";
正如我们稍后将看到的,第二阶段的孩子试图减少他们的信号量来等待他的兄弟。通过将值从零开始,当第二阶段的孩子尝试这些减量时,操作系统将使孩子进入睡眠状态,因为。只有在所有第一阶段孩子都退出并释放信号量后,系统才会解锁第二阶段孩子。
# start in blocked state
$sem->setall((0) x $FIRST_STAGE_CHILDREN);
首先我们fork
是第一阶段的孩子。在这种设计中,父进程会尽可能多地进行簿记。这保持了first_stage
和second_stage
简单的定义。此外,如果第一阶段的孩子在没有释放信号量的情况下以某种方式退出,那么第二阶段将没有运行的希望。
my %kids;
foreach my $id (0 .. $FIRST_STAGE_CHILDREN - 1) {
my $pid = fork;
die "$0: fork: $!" unless defined $pid;
if ($pid) {
++$kids{$pid};
}
else {
first_stage $id;
$sem->op($id, 1, 0); # release
exit 0;
}
}
现在我们分叉第二阶段的孩子。重要提示:虽然代码对多个信号量执行操作,但这会原子地发生,也就是说,它要么对所有信号量起作用,要么对它们都不起作用。在任何可观察的状态下,似乎第二阶段能够抓取的信号量都少于第一阶段的所有信号量。这是一个重要的属性。在更复杂的系统中,随意的 onesie-twosie 获取和释放将导致死锁。
my $pid = fork;
die "$0: fork: $!" unless defined $pid;
if ($pid) {
++$kids{$pid};
}
else {
# block waiting on all first-stage children
my @op = map +($_, -1, 0), 0 .. $FIRST_STAGE_CHILDREN - 1;
$sem->op(@op);
second_stage;
exit 0;
}
最后,父进程等待所有子进程完成。
do {
$pid = waitpid -1, 0;
print "[$$] reaped $pid\n";
warn "$0: unknown child $pid" unless delete $kids{$pid};
} while $pid > 0 && keys %kids;
示例输出如下。在可以看到停顿的地方观看直播会更有趣。
[18389] 来自孩子的你好 0
[18390] 孩子 1 你好
[18390] 孩子 1 完成
[18388] 收获 18390
[18389] 孩子 0 完成
【18391】二期孩子你好!
[18388] 收获 18389
[18388] 收获 18391