2013-01-18 58 views
2

所以我有一个程序,我从中产生了一些做一些有用任务的孩子。然后,我产生了另一个孩子,在开始工作之前需要等待第一个孩子停下来。然后父程序继续运行,最后等待最后一个分叉的孩子停下来。可以让一个perl孩子等待另一个perl孩子完成吗?

我遇到了需要等待其他人的孩子没有的问题。

use strict; 
use warnings; 
use diagnostics; 

my $pid1; 
my $child1 = fork(); 
if ($child1) { 
    # parent 
    #print "pid is $pid, parent $$\n"; 
    $pid1 = $child1; 
} elsif ($child1 == 0) { 
     # child1 
     # do something 
     sleep 20; 
     print "Child1\n"; 

     exit 0; 
} else { 
     die "couldnt fork: $!\n"; 
} 

my $pid2; 
my $child2 = fork(); 
if ($child2) { 
    # parent 
    #print "pid is $pid, parent $$\n"; 
    $pid2 = $child2; 
} elsif ($child2 == 0) { 
     # child2 
     # wait for child1 to finish 
     my $tmp = waitpid($pid1, 0); 

     # do something else 
     print "Child2\n"; 

     exit 0; 
} else { 
     die "couldnt fork: $!\n"; 
} 

# do more stuff 

# wait for child2 to finish 
my $tmp = waitpid($pid2, 0); 

有没有简单的方法来做到这一点?可能没有必要在第二个包裹第一个孩子?

+0

你可以让父母等待child1在产卵之前完成child2吗?是否有要求在child1完成之前产生child2? – Joel

+0

我之所以需要等待,是因为产卵后的父母所做的事情在时间上有些敏感。 –

回答

1

这样做的简单方法是使用Forks::Super

use Forks::Super; 
my $child1 = fork(); 
if ($child1 != 0) { 
    # ... parent code ... 
} else { 
    # ... child code ... 
    exit; 
} 

my $child2 = fork { 
    depend_on => $child1, 
    on_busy => 'queue', 
    sub => sub { 
     # ... code to execute in 2nd child ... 
    } 
}; 
# ... more code to run in the parent ... 
# ... and at the end of the program: 
waitall; 

Forks::Superwaitpid仍称在父(幕后)。但是当第一个孩子完成时,Forks::Super将知道是时候开始在后台启动第二个子进程。

2

在类Unix系统中,给定进程只能等待自己的子进程死掉。它不能等待兄弟姐妹,祖先或孙辈死去。

2

生成一个孩子,让该过程产生子进程,然后等待它们完成,然后再继续执行第一个子进程。

然后让父母完成工作并等待子进程准备好等待。

1

假设你的perlsemop和 的朋友,你可以使用System V信号量在孩子之间进行同步。下面的 为一个工作示例程序。

我们从平常的事情开始。代码使用内置的 IPC::SysVIPC::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_stagesecond_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; 
    } 
} 

现在我们分叉第二阶段的孩子。重要提示:尽管代码 对多个信号量执行操作,但原子上会发生, ,也就是说,它可以适用于所有这些信号,也可以不适用于它们。在没有 可观察到的状态下,它会显示第二阶段能够抢占第一阶段信号量的任何少于全部的 。这是一个重要的 属性。在更复杂的系统中,随意发布和释放将导致死锁。

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] hello from child 0 
[18390] hello from child 1 
[18390] child 1 done 
[18388] reaped 18390 
[18389] child 0 done 
[18391] hello from second-stage child! 
[18388] reaped 18389 
[18388] reaped 18391
相关问题