2013-01-18 104 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
相關問題