Multithreading Perl-Capture:微型线程和线程

Multithreading Perl-Capture:微型线程和线程,multithreading,perl,Multithreading,Perl,我正在试验从子进程捕获标准输出/错误的不同方法。我当前的解决方案使用open3。它在大多数情况下都能工作,但在某些情况下会导致死锁,尤其是由于某些原因,它经常发生在Windows上。我正在尝试用Capture::Tiny替换它。它似乎没有这个僵局问题,但还有另一个问题。它不能很好地处理线程。我需要从线程运行子进程。主线程的输出被重新定向,并与从另一个线程启动的子进程的输出合并。文件说: 文件句柄是全局的。在没有协调的情况下,在不同线程中混合I/O和捕获将导致问题 我找不到有关“协调”的任何细节。

我正在试验从子进程捕获标准输出/错误的不同方法。我当前的解决方案使用open3。它在大多数情况下都能工作,但在某些情况下会导致死锁,尤其是由于某些原因,它经常发生在Windows上。我正在尝试用Capture::Tiny替换它。它似乎没有这个僵局问题,但还有另一个问题。它不能很好地处理线程。我需要从线程运行子进程。主线程的输出被重新定向,并与从另一个线程启动的子进程的输出合并。文件说:

文件句柄是全局的。在没有协调的情况下,在不同线程中混合I/O和捕获将导致问题

我找不到有关“协调”的任何细节。也许有人可以解释一下,这里可以应用什么样的协调? 下面是一个简化的示例,它演示了问题:

##### test.pl
use strict;
use warnings;
use threads;
use Capture::Tiny qw(:all);
use Data::Dumper;

sub RunCmd
{
    my ($cmdArr) = @_;
    my $exe = shift @$cmdArr;
    my $tid = threads->tid();
    open my $outFh, "+>out_$tid.tmp" or die "Cannot open tmp out: $!";
    open my $errFh, "+>err_$tid.tmp" or die "Cannot open tmp err: $!";
    my ($out, $err, $exitCode) = capture { system($exe, @$cmdArr); } stdout => $outFh, stderr => $errFh;
    close $outFh;
    close $errFh;
    $exitCode = $exitCode >> 8;
    return ($exitCode == 0 ? 1 : 0, [ split(/(?<=\n)/, $out) ], [ split(/(?<=\n)/, $err) ], $exitCode);
}

sub StartJob
{
    my ($job) = @_;
    my($ret, $out, $err, $exitCode) = RunCmd($job->{'cmd'});
    $job->{'ret'} = $ret;
    $job->{'out'} = $out;
    $job->{'err'} = $err;
    $job->{'exitCode'} = $exitCode;
    return $job;
}

sub Execute
{
    my ($cmdList) = @_;
    my @threads = ();
    foreach (@$cmdList)
    {
        my $job = { 'cmd' => $_ };
        my $t = threads->new(sub { return StartJob($job) });
        push @threads, $t;
    }
    my @output = ();
    foreach my $t (@threads)
    {
        my $result = $t->join();
        push @output, $result;
    }
    return \@output;
}

my $commands = [
    [ $^X, 'myScript.pl', 'command1' ],
    [ $^X, 'myScript.pl', 'command2' ]
];

my $output = Execute($commands);
print Dumper($output);             # this output goes to out_1.tmp file
##### end of test.pl


##### myScript.pl
use strict;
use warnings;

my $cmd = $ARGV[0] // 'UNDEF';
foreach (1..5)
{
    sleep 1;
    print "$cmd: $_\n";
}

$cmd eq 'command2' and print STDERR "Error in $cmd\n"; 
##### end of myScript.pl
test.pl
严格使用;
使用警告;
使用线程;
使用Capture::Tiny qw(:全部);
使用数据::转储程序;
子RunCmd
{
我的($cmdArr)=@;
my$exe=shift@$cmdArr;
my$tid=threads->tid();
打开我的$OUTH,“+>out_$tid.tmp”或die“无法打开tmp out:$!”;
打开我的$errFh,“+>err_u$tid.tmp”或“无法打开tmp err:$!”;
my($out,$err,$exitCode)=捕获{system($exe,@$cmdArr);}stdout=>$outph,stderr=>$errFh;
关闭$outph;
收盘价$errFh;
$exitCode=$exitCode>>8;
返回($exitCode==0?1:0,[split(/(?{'ret'}=$ret;
$job->{'out'}=$out;
$job->{'err'}=$err;
$job->{'exitCode'}=$exitCode;
返回$job;
}
副执行
{
我的($cmdList)=@;
我的@threads=();
foreach(@$cmdList)
{
我的$job={'cmd'=>$\uz};
my$t=threads->new(sub{returnstartjob($job)});
推送@threads,$t;
}
我的@output=();
每个我的$t(@threads)
{
我的$result=$t->join();
按@output$result;
}
返回\@输出;
}
我的$commands=[
[$^X,'myScript.pl','command1'],
[$^X,'myScript.pl','command2']
];
my$output=执行($commands);
打印转储程序($output);35;此输出转到out_1.tmp文件
#####test.pl结束
#####myScript.pl
严格使用;
使用警告;
my$cmd=$ARGV[0]/'UNDEF';
foreach(1..5)
{
睡眠1;
打印“$cmd:$\n”;
}
$cmd eq'command2'并打印STDERR“Error in$cmd\n”;
#####myScript.pl的结尾

一种不太容易出现问题且效率更高的方法是使用类似的方法。Capture::Tiny必然会更改流程的STDERRA一种不太容易出现问题且效率更高的方法是使用类似的方法。Capture::Tiny必然会更改流程的STDERR