在Linux上通过SSH运行多线程Perl脚本的问题

时间:2011-05-03 10:47:41

标签: multithreading perl ssh io

如果我通过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特有的。

有人可以解释一下这里有什么问题吗?

2 个答案:

答案 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,以便线程局部缓冲区始终为空。