2012-01-05 131 views
2

我有两个代码如何并行运行父命令中的两条命令?

1.

use File::Temp qw(tempfile); 
$tmp = new File::Temp(UNLINK => 0); 
system("tv_grab_au | tv_sort >> $file"); 
system("cp $file $HOME/.xmltv/listings.xml"); 

unlink($file); 

2.

while (-e $file) { 
sleep 2; 
system("tvtime-command DISPLAY_MESSAGE \'Updating TV Guide. Please wait this might take a several minutes...\'"); 
} 

我想这2个代码运行tv_grab_au XMLTV采集(更新电视指南)同时结合,而且,发送命令到tvtime以显示消息'更新电视指南。请等待这可能需要几分钟......',每两秒钟,直到$文件存在。

我试试这个:

use strict; 
use warnings; 
use File::Temp qw(tempfile); 
my $file = new File::Temp(UNLINK => 0); 
use POSIX qw(:sys_wait_h); 
$|++; 

defined(my $pid = fork) or die "Couldn't fork: $!"; 

if (!$pid) {  
    system("tv_grab_huro | tv_sort >> $file"); 
    unlink($file); 
} 
else { 
    while (! waitpid($pid, WNOHANG)) { 
     system("tvtime-command DISPLAY_MESSAGE \'Updating TV Guide. Please wait this might take a several minutes...\'"); 
     sleep 2; 
     } 
} 

感谢。

回答

4

内置函数fork函数在新的后台进程中创建当前程序的副本。原来的过程和“孩子”过程将同时运行。所以,你可以这样做:

use File::Temp qw(tempfile); 
my $file = new File::Temp(UNLINK => 0); 

my $new_pid = fork(); 
die "fork failed $!" unless defined $new_pid; # this is uncommon 

# Task 1 - in the background 
if ($new_pid == 0) { 
    system("tv_grab_au | tv_sort >> $file"); 
    system("cp $file $HOME/.xmltv/listings.xml");  
    unlink($file); 
    exit;   # don't forget this part! 
} 

# Task 2 - in the foreground 
while (-e $file) { 
    print "..."; 
    sleep 2; 
} 

使用$file当第一任务已完成的指标也有一些缺点。如果子代码有一些运行时错误怎么办?如果子进程中断?子进程可能会在它有机会删除$file之前退出。然后你的父进程中的while循环永远不会结束。

内置waitpid命令可以检查子进程是否仍在运行,并且可以处理子进程异常终止的情况。

# Task 2 
use POSIX ':sys_wait_h'; 
while (! waitpid $new_pid, &WNOHANG) { # WNOHANG => non-blocking wait 
    print "..."; 
    sleep 2; 
} 
0

使用fork()。我添加了额外的sleep()调用,以便您可以看到这些进程既运行又正常工作。在实践中,crontab更新可能会运行得足够快,以至于监视器循环根本不运行,或者只运行一次。我使用“除非(...)”,因为在我看来,使代码更清晰;需要记住的是fork()将pid返回给父对象,并将其返回给子对象。因此没有看到pid的进程是一个子进程。 (正如已经指出的那样,如果fork失败,fork将返回undef,并且代码将在原始进程中执行。在我们的例子中,这仅仅意味着在写入完成后启动监控,所以唯一的我们失去的东西就是监测。)

my $file = "/tmp/.$$.crontab.txt"; 
my $crontab = <<EOS; 
# Crontab lines here. Inserted at @{[scalar localtime()]} 
EOS 

my ($writer_pid, $monitor_pid); 

$|++; 

# Open file BEFORE launching processes. The monitor depends on the file's 
# presence or absence, so if we opened it in the writer process, there'd be a 
# chance the monitor process would check before we created it, and exit without 
# monitoring. 
die "Cannot open temp file\n" unless open(WRITE, ">" . $file); 

# Crontab file handle gets passed to the forked process, so we can just use it. 
# Altered so we can see the process do its thing. 
unless ($writer_pid = fork()) { 
     print WRITE $crontab."\n"; 
     close WRITE; 
     print("crontab -l |grep -v backup >> $file"); 
     sleep 20; 
     print("crontab $file"); 
     sleep 10; 
     unlink($file); 
     print "done!\n"; 
     exit; 
} 

# Either file will exist, or the previous process will 
# have completed. If it exists, we monitor. If not, 
# we exit immediately. 
unless ($monitor_pid = fork()) { 
    # Child: monitor the writer. 
    my $waitcount = 1; 
    while (-e $file) { 
     sleep 2; 
     print "($waitcount) installing crontab..."; 
     $waitcount++; 
    } 
    print "installed\n"; 
    exit; 
} 

waitpid($monitor_pid, 0); 
waitpid($writer_pid,0); 
print "both processes done\n"; 
+0

'fork'给孩子返回“0”。如果fork失败,它只会返回'undef'。 – mob 2012-01-05 22:42:57

+0

你是对的,我会进行编辑。 – 2012-01-09 20:31:31