我已经创建了一个包含另一个工具(overlapFeatures)的perl脚本,以便我可以动态地正确转换我的文件格式。我正在处理的文件都是标签分隔表,通常是200万行左右。就其本身而言,overlapFeatures在处理这些问题时没有任何问题。
但是我认为我是通过同时管道这么多线来导致管道锁定的。我知道我需要以某种方式对此进行处理,以便我可以同时读取和写入子进程。但是我真的不明白如何在perl(或任何其他程序)中正确使用线程。据我了解,我可以使用threads
甚至IPC::run
来解决我的问题。
我的原始脚本最终会导致死锁:
use strict;
use warnings;
use IPC::Open2;
my $infile = shift;
my $featurefile = shift;
my $command = 'overlapFeatures';
my @args = (qw (-a stdin -b), $featurefile);
my ($input, $output);
my $pid = open2($output, $input, $command, @args)
or die "Failed with error $!\n";
open (my $infh, '<', $infile) or die "Can't open $infile\n";
while (<$infh>){
# Do some format conversion...
chomp
my @cols = split /\t/;
# print a modified line to the tool
print $input join ("\t", @cols[0,2,3,1,5,4]),"\n";
}
close ($input);
while (<$output>){
# format conversion for ouput
chomp;
my @cols = split /\t/;
print join (",",@cols[0,1,2,5,3,8]),"\n";
}
close ($output);
我尝试重写脚本以按照How to filter a lot of data with IPC::Open2?使用线程,如下所示:
use strict;
use warnings;
use IPC::Open2;
use threads;
my $infile = shift;
my $featurefile = shift;
my $command = 'overlapFeatures';
my @args = (qw (-a stdin -b), $featurefile);
my ($input, $output);
my $pid = open2($output, $input, $command, @args)
or die "Failed with error $!\n";
my $thread = async {
print join(",", qw(seqid start end strand read feature name)),"\n";
for(;;) {
my $line = <$output>; # should block here and wait for output?
last if !defined $line; # end of stream reached?
print STDERR "Got line $line\n";
# Do some format conversion...
chomp $line;
my @cols = split /\t/, $line;
# print a modified line to the tool
print join(",",@cols[0,1,2,5,3,8]),"\n";
}
close($output)
};
{
open (my $infh, '<', $infile) or die "Can't open $infile\n";
while (<$infh>){
# format conversion for ouput
chomp;
my @cols = split /\t/;
print $input join ("\t", @cols[0,2,3,1,5,4]),"\n";
}
close ($input);
}
$thread->join();
waitpid ($pid, 0);
但是,脚本仍然以同样的方式卡住,我也被卡住了。我也无法弄清楚如何在这个例子中使用IPC::run
。
我做错了什么?我误解了线程吗?
编辑:花更多时间调试脚本(以及来自amon的帮助),我发现我能够从$output
检索行。但是,脚本永远不会完成,并且在收到所有输出后似乎挂起。我认为现在这是我唯一的问题。
答案 0 :(得分:1)
这更像是一个长篇评论。
我在精简版中尝试了您的代码。我删除了转换代码,使用Unix yes
命令作为无限数据源并将输出打印到/dev/null
,因为我们目前对输出不感兴趣,但是在程序工作中。作为overlapFeatures
的替代品,我使用cat
来传递未更改的数据。
use strict; use warnings; use IPC::Open2; use threads;
my $command = "cat";
my @args = ();
my ($input, $output);
my $pid = open2($output, $input, $command, @args)
or die "Failed with error $!\n";
my $thread = async {
print $_ while defined($_ = <$output>);
close($output)
};
{
my $c=0;
open (my $infh, "-|", "yes") or die;
open my $null, ">/dev/null" or die;
while (<$infh>){
$c++;
print $null $_;
if ($c >= 1_000_000) {
print "\n==another million==\n\n";
$c=0
}
}
close ($input);
}
$thread->join();
waitpid ($pid, 0);
一百万行(字面意思),我打印一条状态消息,断言IO仍在工作。
使用Perl 12.4在Ubuntu Linux上测试,给定的脚本完美无瑕。因此可以合理地假设问题不在于IPC代码,而是在数据格式转换,包装程序或数据质量(yes
输出字符串"1\n"
,每行产生许多小数据行(每组约2MB,每行2字节))
可能是您正在运行其他配置。如果您正在运行* nix,请断言我使用的脚本也适用于您。如果没有,请明确说明此配置并尝试运行等效脚本。
也可以将你的包装器分成两个脚本,至少用于测试,所以你可以运行类似
的东西$ convert-to | overlapFeatures | convert-from
这会将所有IPC委托给shell,并断言转换正在运行,并且架构是可实现的。
其他不可思议的想法集思广益:(1)close
操作何时执行?可能是因为一些奇怪的原因,循环的一端过早退出? print STDERR "Closing down xx\n"
之前的close
可能很有趣。
(2)open2
和async
是否成功生成了他们的进程/线程并且返回控制流?偏执我会在他们之后加上另一个print STDERR
......
(3)您是否从脚本中获取任何数据,或者在一段时间后数据流变干了?
在所有书写结束都是EOF
d之前,管道不会产生close
。因此,所有线程都应关闭它们未使用的任何内容:
my $thread = async {
close $input;
print $_ while defined($_ = <$output>);
close($output)
};
和
{
close $output;
my $c=0;
open (my $infh, "-|", "yes") or die;
open my $null, ">/dev/null" or die;
while (<$infh>){
$c++;
print $null $_;
...