如何在Perl中以线程安全的方式更改当前目录?

时间:2010-09-19 08:42:44

标签: multithreading perl working-directory

我正在使用Thread::Pool::Simple创建一些工作线程。每个工作线程都会做一些事情,包括调用chdir,然后执行外部Perl脚本(来自jbrowse基因组浏览器,如果重要的话)。我使用capturex来调用外部脚本并在其失败时死亡。

我发现当我使用多个线程时,事情开始变得混乱。经过一番研究。似乎某些线程的当前目录不正确。

也许chdir在线程之间传播(即不是线程安全的)? 或者它可能是capturex的东西?

那么,我如何安全地为每个线程设置工作目录?

**更新**

按照建议在执行时更改dir,我想问一下我应该如何将这两个命令传递给capturex

目前我有:

my @args = ( "bin/flatfile-to-json.pl", "--gff=$gff_file", "--tracklabel=$track_label", "--key=$key", @optional_args );
capturex( [0], @args );

如何向@args添加其他命令? capturex会因任何命令的错误而继续死亡吗?

2 个答案:

答案 0 :(得分:2)

认为“当前工作目录”是一个每线程属性。我期待它是该过程的属性。

目前尚不清楚为什么你需要使用chdir。您是否可以启动外部脚本而不是相应地设置 new 进程的工作目录?这听起来更可行。

答案 1 :(得分:2)

我认为你可以通过放弃IPC::System::Simple而不是正确的工具来解决你的“如何在运行命令之前解决孩子问题”这个问题。

而不是做

my $output = capturex($cmd, @args);

做类似的事情:

use autodie qw(open close);
my $pid = open my $fh, '-|';
unless ($pid) { # this is the child
  chdir($wherever);
  exec($cmd, @args) or exit 255;
}
my $output = do { local $/; <$fh> };
# If child exited with error or couldn't be run, the exception will
# be raised here (via autodie; feel free to replace it with 
# your own handling)
close ($fh);

如果你得到一个行列表而不是capturex的标量输出,那么唯一需要改变的是倒数第二行(到my @output = <$fh>;)。

有关forking-open的更多信息,请参阅perldoc perlipc

优先考虑capture("chdir wherever ; $cmd @args")的好处是,它不会让shell有机会对你的@args做坏事。

更新的代码(不捕获输出)

my $pid = fork;
die "Couldn't fork: $!" unless defined $pid;
unless ($pid) { # this is the child
  chdir($wherever);
  open STDOUT, ">/dev/null"; # optional: silence subprocess output
  open STDERR, ">/dev/null"; # even more optional
  exec($cmd, @args) or exit 255;
}
wait;
die "Child error $?" if $?;