1

Windows プラットフォームで、次の非常に単純で小さな Perl スクリプトに問題があります。

use strict;
use warnings;
use threads;
use threads::shared;

my $print_mut : shared;
my $run_mut : shared;
my $counter : shared;

$counter = 30;

###############################################################

sub _print($)
{
lock($print_mut);
my $str = shift;
my $id  = threads->tid();
print "[Thread_$id] $str";
return;
}

###############################################################

sub _get_number()
{
lock($counter);
return $counter--;
}

###############################################################

sub _get_cmd($)
{
my $i = shift;
if ($^O eq 'MSWin32')
  {
    return qq{cmd /c "echo $i"};
  }
return "echo $i";
}

###############################################################

sub thread_func()
{
while ((my $i = _get_number()) > 0)
  {
    my $str = 'NONE';
    {
    lock($run_mut);
    my $cmd = _get_cmd($i);
    $str = `$cmd`;
    }
    chomp $str;
    _print "Got string: '$str'.\n";
  }
return;
}

###############################################################

# Start all threads
my @threads;
for (1 .. 8)
  {
my $thr = threads->create('thread_func');
push @threads, $thr;
  }

# Wait for completion of the threads
foreach (@threads)
  {
$_->join;
  }

###############################################################

私の Linux ボックス (Perl v5.10.0) では、正しい (期待される) 結果が得られます。

$ perl ~/tmp/thr2.pl
[Thread_1] 文字列を取得しました: '30'。
[Thread_1] 文字列 '29' を取得しました。
[Thread_2] 文字列を取得しました: '28'。
[Thread_1] 文字列を取得しました: '27'。
[Thread_2] 文字列を取得しました: '26'。
[Thread_1] 文字列を取得しました: '25'。
[Thread_1] 文字列を取得しました: '23'。
[Thread_2] 文字列を取得しました: '24'。
[Thread_2] 文字列を取得しました: '20'。
[Thread_2] 文字列を取得しました: '19'。
[Thread_1] 文字列 '22' を取得しました。
[Thread_4] 文字列「18」を取得しました。
[Thread_5] 文字列「15」を取得しました。
[Thread_2] 文字列を取得しました: '17'。
[Thread_2] 文字列 '12' を取得しました。
[Thread_3] 文字列を取得しました: '21'。
[Thread_4] 文字列 '14' を取得しました。
[Thread_4] 文字列 '7' を取得しました。
[Thread_1] 文字列 '16' を取得しました。
[Thread_6] 文字列 '11' を取得しました。
[Thread_2] 文字列 '10' を取得しました。
[Thread_2] 文字列 '2' を取得しました。
[Thread_3] 文字列 '8' を取得しました。
[Thread_5] 文字列 '13' を取得しました。
[Thread_8] 文字​​列 '6' を取得しました。
[Thread_4] 文字列 '5' を取得しました。
[Thread_1] 文字列 '4' を取得しました。
[Thread_6] 文字列 '3' を取得しました。
[Thread_7] 文字列 '9' を取得しました。
[Thread_2] 文字列 '1' を取得しました。
$

ただし、Windows (Perl v5.10.1) では混乱します。

C:\>perl Z:\tmp\thr2.pl
[Thread_1] 文字列を取得しました: '30'。
[Thread_2] 文字列を取得しました: '29'。
[Thread_2] 文字列を取得しました: '21'。
[Thread_6] 文字列 '26' を取得しました。
[Thread_5] 文字列 '25' を取得しました。
[Thread_5] 文字列「17」を取得しました。
[Thread_8] 文字​​列 '23' を取得しました。
[Thread_1] 文字列 '22' を取得しました。
[Thread_1] 文字列を取得しました: '14'。
[Thread_2] 文字列を取得しました: '20'。
[Thread_6] 文字列「18」を取得しました。
[Thread_7] 文字列 '24' を取得しました。
[Thread_7] 文字列 '9' を取得しました。
[Thread_8] 文字​​列 '15' を取得しました。
[Thread_3] 文字列 '28' を取得しました。
[Thread_3] 文字列 '6' を取得しました。
[Thread_4] 文字列 '12' を取得しました。
[Thread_2] 取得した文字列: '[Thread_4] 取得した文字列: '27'。
19'.
[Thread_6] 文字列 '10' を取得しました。
[Thread_5] 文字列「16」を取得しました。
[Thread_7] 文字列 '8' を取得しました。
[Thread_8] 文字​​列 '7' を取得しました。
[Thread_1] 文字列を取得しました: '13'。
[Thread_3] 文字列 '5' を取得しました。
[Thread_4] 文字列 '4' を取得しました。
[Thread_2] 文字列 '11' を取得しました。
[Thread_6] 取得した文字列: '[Thread_2] 取得した文字列: '3'。
[Thread_5] 文字列 '2' を取得しました。
1'.

C:\>

この問題は、スレッド関数からバックティックを介してコマンド (コマンドは関係ありません) を実行して出力を収集すると発生します。

Perl のスレッドと Windows 上の Perl の経験は非常に限られています。私はいつも Perl でスレッドをまったく使用しないように努めてきましたが、今回はそれらを使用する必要があります。

perldoc と Google で答えを見つけることができませんでした。誰かが私のスクリプトの何が問題なのか説明してもらえますか?

前もって感謝します!

4

1 に答える 1

1

WinXP でこの問題を再現できますが、結果は同じです。ただし、STDOUT のみに影響するようです。

Dmitry が提案したように、ファイルに出力しても問題は発生しません。また、STDERR を使用しても問題は発生しません。ただし、STDOUT とファイルに書き込むと表示されます。これは手がかりです。

プリントに別のバックティック変数を追加すると、各連結の前の 2 つの場所で問題が発生します。

テスト中に chomp() が不十分であると判断したため、追加しました

$str =~ s/[^\w]+//g;

この興味深い結果で:

[Thread_6] Got string: 'Thread_4Gotstring1925'.

$strこれは、実際には別のスレッドからの印刷バッファー全体を保持していることを意味しているようです。控えめに言っても、これは奇妙です。

これがなければ...

2 つのスレッドがまったく同時に実行されます。

print "[Thread_4] Got string: '19'.\n"
$str = `echo 25`

print と echo はおそらく同じ STDOUT バッファを共有するため、すべてが に入り$str、結果の printが表示されます。

chomp "[Thread_4] Got string: '19'.\n25\n"
print "[Thread_6] Got string: [Thread_4] Got string: ''19'\n25'.\n"

要するに、Windowsの問題です。問題を「修正」したい場合は、echo と print の両方がロックされた値でカバーされていることを確認してください。}thread_func下に移動すると_print、きれいな印刷が得られます。すなわち:

{
    lock($run_mut);
    my $cmd = _get_cmd($i);
    $str = `$cmd`;
    chomp $str;
    _print "Got string: '$str'.\n";
}

これを確認するおもしろい方法は、echo を STDERR に書き込む Windows コマンドに置き換え、それが perl 内の STDERR への出力と衝突するかどうかを確認することです。

于 2011-04-30T08:56:14.493 に答える