multithreading - Problem med backticks i multi-threaded Perl script på Windows

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har problemer med følgende meget enkle og små Perl script på Windows platform.


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'.
";
  }
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;
  }

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


På min Linux-boks (Perl v5.10.0) får jeg korrekte (forventede) resultater:


$ perl ~/tmp/thr2.pl 
[Thread\_1] Got string: '30'.
[Thread\_1] Got string: '29'.
[Thread\_2] Got string: '28'.
[Thread\_1] Got string: '27'.
[Thread\_2] Got string: '26'.
[Thread\_1] Got string: '25'.
[Thread\_1] Got string: '23'.
[Thread\_2] Got string: '24'.
[Thread\_2] Got string: '20'.
[Thread\_2] Got string: '19'.
[Thread\_1] Got string: '22'.
[Thread\_4] Got string: '18'.
[Thread\_5] Got string: '15'.
[Thread\_2] Got string: '17'.
[Thread\_2] Got string: '12'.
[Thread\_3] Got string: '21'.
[Thread\_4] Got string: '14'.
[Thread\_4] Got string: '7'.
[Thread\_1] Got string: '16'.
[Thread\_6] Got string: '11'.
[Thread\_2] Got string: '10'.
[Thread\_2] Got string: '2'.
[Thread\_3] Got string: '8'.
[Thread\_5] Got string: '13'.
[Thread\_8] Got string: '6'.
[Thread\_4] Got string: '5'.
[Thread\_1] Got string: '4'.
[Thread\_6] Got string: '3'.
[Thread\_7] Got string: '9'.
[Thread\_2] Got string: '1'.
$


Men på Windows (Perl v5.10.1) får jeg et rod:


C:>perl Z:	mp	hr2.pl
[Thread\_1] Got string: '30'.
[Thread\_2] Got string: '29'.
[Thread\_2] Got string: '21'.
[Thread\_6] Got string: '26'.
[Thread\_5] Got string: '25'.
[Thread\_5] Got string: '17'.
[Thread\_8] Got string: '23'.
[Thread\_1] Got string: '22'.
[Thread\_1] Got string: '14'.
[Thread\_2] Got string: '20'.
[Thread\_6] Got string: '18'.
[Thread\_7] Got string: '24'.
[Thread\_7] Got string: '9'.
[Thread\_8] Got string: '15'.
[Thread\_3] Got string: '28'.
[Thread\_3] Got string: '6'.
[Thread\_4] Got string: '12'.
[Thread\_2] Got string: '[Thread\_4] Got string: '27'.
19'.
[Thread\_6] Got string: '10'.
[Thread\_5] Got string: '16'.
[Thread\_7] Got string: '8'.
[Thread\_8] Got string: '7'.
[Thread\_1] Got string: '13'.
[Thread\_3] Got string: '5'.
[Thread\_4] Got string: '4'.
[Thread\_2] Got string: '11'.
[Thread\_6] Got string: '[Thread\_2] Got string: '3'.
[Thread\_5] Got string: '2'.
1'.

C:>


Problemet opstår, når jeg kører en kommando (betyder ikke noget hvad kommando) fra trådfunktionen via backtick for at indsamle det s output.


Jeg har meget begrænset erfaring med tråde i Perl og med Perl på Windows.
Jeg har altid forsøgt at undgå at bruge tråde i Perl overhovedet, men denne gang skal jeg bruge dem.


Jeg kunne ikke finde svaret i perldoc og Google.
Kan nogen venligst forklare, hvad der er galt med mit script?


Tak på forhånd!

Bedste reference


Jeg kan genskabe dette problem på min WinXP, med identiske resultater. Det ser imidlertid ud til kun at påvirke STDOUT.


Problemet vises ikke, hvis jeg udskriver til en fil, og det vises heller ikke, når jeg bruger STDERR, som Dmitry foreslog. Det ser imidlertid ud, hvis jeg skriver til STDOUT og en fil. Hvilket er en anelse.


Hvis du tilføjer en anden backtick-variabel til udskriften, får du problemet to gange inden hver sammenkædning.


Under testningen besluttede jeg at chomp () var utilstrækkelig, så jeg tilføjede


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


Med dette interessante resultat:


[Thread\_6] Got string: 'Thread\_4Gotstring1925'.


Hvilket så synes at indebære, at $str faktisk holder hele printbufferen fra en anden tråd. Hvilket er mærkeligt, mindst sagt.


Medmindre dette ...


To tråde løber, på det nøjagtigt samme tidspunkt:


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


Udskriv og ekko deler sandsynligvis den samme STDOUT-buffer, og så går det hele ind i $str med den resulterende udskrift:


chomp "[Thread\_4] Got string: '19'.
25
"
print "[Thread\_6] Got string: [Thread\_4] Got string: ''19'
25'.
"


Kort sagt et Windows-problem. Hvis du vil 'løse' problemet, skal du sørge for, at ekko og udskrivning begge er dækket af låste værdier. Flytning af } i thread\_func nede under \_print skal give en ren udskrift. dvs .:


{
    lock($run\_mut);
    my $cmd = \_get\_cmd($i);
    $str = `$cmd`;
    chomp $str;
    \_print "Got string: '$str'.
";
}


En sjov måde at bekræfte dette på ville være at erstatte ekko med nogle Windows-kommandoer, der skriver til STDERR, og se om det kolliderer med et print til STDERR inden for perl.