如果我通过SSH远程运行它,我无法理解以下简单Perl脚本的行为。
use strict;
use warnings;
use threads;
use threads::shared;
use POSIX;
my $print_mutex : shared;
################################################################################
sub _print($)
{
my $str = shift;
lock($print_mutex);
my $id = threads->tid();
my $time = strftime('%H:%M:%S', localtime time);
print "$time [$id] $str";
return;
}
################################################################################
sub run()
{
for my $i (1 .. 3)
{
_print("Begin $i\n");
sleep 1;
_print("End $i\n");
}
return threads->tid();
}
################################################################################
_print "Starting test.\n";
my @threads;
for my $thr_num (1 .. 2)
{
my $thr = threads->create('run');
push @threads, $thr;
_print "Thread created.\n";
}
foreach (@threads)
{
my $id = $_->join;
_print "Thread '$id' finished.\n";
}
_print "Test finished.\n";
################################################################################
当我在使用Perl-5.10.0的Linux机器上正常运行时,我得到了预期的结果:
$ perl /tmp/a.pl 14:25:54 [0] Starting test. 14:25:54 [0] Thread created. 14:25:54 [1] Begin 1 14:25:54 [0] Thread created. 14:25:54 [2] Begin 1 14:25:55 [1] End 1 14:25:55 [1] Begin 2 14:25:55 [2] End 1 14:25:55 [2] Begin 2 14:25:56 [1] End 2 14:25:56 [1] Begin 3 14:25:56 [2] End 2 14:25:56 [2] Begin 3 14:25:57 [1] End 3 14:25:57 [0] Thread '1' finished. 14:25:57 [2] End 3 14:25:57 [0] Thread '2' finished. 14:25:57 [0] Test finished. $
然而,当我通过SSH(在同一本地主机上运行它,但没关系)时,我得到了非常奇怪的结果(仔细查看时间戳和线程ID):
$ ssh localhost 'perl /tmp/a.pl' 14:26:11 [0] Starting test. 14:26:11 [0] Thread created. 14:26:11 [1] Begin 1 14:26:12 [1] End 1 14:26:12 [1] Begin 2 14:26:13 [1] End 2 14:26:13 [1] Begin 3 14:26:14 [1] End 3 14:26:11 [2] Begin 1 14:26:12 [2] End 1 14:26:12 [2] Begin 2 14:26:13 [2] End 2 14:26:13 [2] Begin 3 14:26:14 [2] End 3 14:26:11 [0] Thread created. 14:26:14 [0] Thread '1' finished. 14:26:14 [0] Thread '2' finished. 14:26:14 [0] Test finished. $
我从未在单线程Perl脚本中看到过这种情况,我注意到在第一个线程创建后我就开始看到I / O的问题了。
我能够在Windows上使用最新的Perl-5.12重现问题,所以我认为问题不是Perl / OS特有的。
有人可以解释一下这里有什么问题吗?
答案 0 :(得分:1)
我自己能够重现这一点。但是,当通过ssh从shell运行它时,我得到了预期的行为。那有什么区别?一个伪终端!
试试这个:
ssh -t localhost 'perl /tmp/a.pl'
答案 1 :(得分:1)
实际上,看起来每个Perl线程都有自己的输出缓冲区。 我已将输出重定向到文件(与通过SSH运行脚本相同,因为它只是禁用行缓冲)并在strace下运行脚本:
$ strace -fF -tt -s200 bash -c "perl /tmp/a.pl > OUT" 2>&1 | grep write [pid 359] 12:12:24.674142 write(1, "12:12:24 [0] Starting test.\n"..., 28) = 28 [pid 359] 12:12:24.687319 write(1, "12:12:24 [0] Thread created.\n"..., 29) = 29 [pid 360] 12:12:27.693225 write(1, "12:12:24 [1] Begin 1\n12:12:25 [1] End 1\n12:12:25 [1] Begin 2\n12:12:26 [1] End 2\n12:12:26 [1] Begin 3\n12:12:27 [1] End 3\n"..., 120) = 120 [pid 361] 12:12:27.706137 write(1, "12:12:24 [2] Begin 1\n12:12:25 [2] End 1\n12:12:25 [2] Begin 2\n12:12:26 [2] End 2\n12:12:26 [2] Begin 3\n12:12:27 [2] End 3\n"..., 120) = 120 [pid 359] 12:12:27.711343 write(1, "12:12:24 [0] Thread created.\n12:12:27 [0] Thread '1' finished.\n12:12:27 [0] Thread '2' finished.\n12:12:27 [0] Test finished.\n"..., 125) = 125 $
很明显,每个线程将所有数据放入线程本地缓冲区,然后(在此示例中,在线程终止之前)调用该缓冲区上的“写入”系统调用。 恕我直言,线程局部输出缓冲区非常坏主意,因为即使你明确序列化“打印”调用,人们也会得到令人困惑的结果。
我找到的解决方案是使用显式序列化并在STDOUT上启用autoflush,以便线程局部缓冲区始终为空。