4

PerlがbashでCtrl-C(sigint)をトラップする方法を見ながら; なぜスレッドで失敗するのか迷っています。私は次のスクリプトを試しています:

#!/usr/bin/env perl

use threads;
use threads::shared; # for shared variables

my $cnt :shared = 0;

sub counter() {
  while (1) {
    $cnt++;
    print "thread: $cnt \n";
    sleep 1;
  }
}

sub finisher{
  ### Thread exit! ...
  print "IIII";
  threads->exit();
  die;
};

# any of these will cause stop of reaction to Ctrl-C
$SIG{INT} = \&finisher;
$SIG{INT} = sub {print "EEE\n" ;} ;
$SIG{INT} = 'IGNORE';

# setting to DEFAULT, brings usual behavior back
#~ $SIG{INT} = 'DEFAULT';

my $mthr = threads->create(\&counter);
$mthr->join();

...そしてSIGINTハンドラーがデフォルト以外に設定されるとすぐに(Ctrl-Cによってexitが発生します)、基本的にスクリプトはCtrl-Cでの反応を停止します。

$ ./test.pl 
thread: 1 
^Cthread: 2 
^C^Cthread: 3 
^C^C^C^Cthread: 4 
thread: 5 
thread: 6 
thread: 7 
thread: 8 
Terminated

...そして私はsudo killall perlスクリプトを終了するためにそうしなければなりません。

これらのリンクには、スレッドとCtrl-Cに関するビットがあります。

...しかし、bashのperlの下でCtrl-Cを「キャプチャ」することが絶対に不可能かどうかが決定的に答えられるかどうかはわかりません。

よろしくお願いします、
乾杯!


わかりました、私はそれを手に入れたと思います(しかし、私は参考のために前のエントリ(以下)を残しています...

秘訣は、メインのSIGINTハンドラーから、kill-を介してスレッドにシグナルを送信する必要があることです。次に、スレッドには(OPの最初のリンクからの)別のSIGINTハンドラーも必要です。そして、単にの代わりに、@ikegamiによる回答join()のコードを使用する必要があります。

#!/usr/bin/env perl

use threads;
use threads::shared; # for shared variables

my $cnt :shared = 0;
my $toexit :shared = 0;


sub counter() {
  $SIG{'INT'} = sub { print "Thread exit\n"; threads->exit(); };
  my $lexit = 0;
  while (not($lexit)) {
    { lock($toexit);
    $lexit = $toexit;
    }
    $cnt++;
    print "thread: $cnt \n";
    sleep 1;
  }
  print "out\n";
}

my $mthr;

sub finisher{
  { lock($toexit);
  $toexit = 1;
  }
  $mthr->kill('INT');
};

$SIG{INT} = \&finisher;

$mthr = threads->create(\&counter);


print "prejoin\n";
#~ $mthr->join();

while (threads->list()) {
   my @joinable = threads->list(threads::joinable);
   if (@joinable) {
      $_->join for @joinable;
   } else {
      sleep(0.050);
   }
}
print "postjoin\n";

私はそこでそれをやり過ぎているかもしれません$toexitが、少なくとも今これは結果です:

$ ./test.pl
prejoin
thread: 1 
thread: 2 
thread: 3 
^CThread exit
postjoin

解決策をありがとうございました:)
乾杯!

 


PERL_SIGNALS@mobによるtoの提案のおかげでunsafePerl5.14は$ENV {'PERL_SIGNALS'}の「内部」設定を許可していません)、どこかに到達しています-現在Ctrl-Cが検出されています-しかし、どちらかで終了しますsegfault、またはエラーあり:

#!/usr/bin/env perl

use threads;
use threads::shared; # for shared variables

my $cnt :shared = 0;
my $toexit :shared = 0;

sub counter() {
  my $lexit = 0;
  while (not($lexit)) {
    { lock($toexit);
    $lexit = $toexit;
    }
    $cnt++;
    print "thread: $cnt \n";
    sleep 1;
  }
  print "out\n";
  #~ threads->detach(); # Thread 1 terminated abnormally: Cannot detach a joined thread
  #~ exit;
}

my $mthr;

# [http://code.activestate.com/lists/perl5-porters/164923/ [perl #92246] Perl 5.14 does not allow "internal" setting of $ENV ...]
sub finisher{
  ### Thread exit! ...
  #~ print "IIII";
  # anything here results with: Perl exited with active threads:
  #~ threads->exit();
  #~ threads->join();
  #~ $mthr->exit();
  #~ $mthr->join();
  #~ $mthr->detach();
  #~ $mthr->kill();
  #~ threads->exit() if threads->can('exit');   # Thread friendly
  #~ die;
  { lock($toexit);
  $toexit = 1;
  }
  #~ threads->join(); #
};




# any of these will cause stop of reaction to Ctrl-C
$SIG{INT} = \&finisher;
#~ $SIG{INT} = sub {print "EEE\n" ; die; } ;
#~ $SIG{INT} = 'IGNORE';

# setting to DEFAULT, brings usual behavior back
#~ $SIG{INT} = 'DEFAULT';

$mthr = threads->create(\&counter);
print "prejoin\n";
$mthr->join();
print "postjoin\n";

上記のコメントで、そのコードは次のように反応します。

$ PERL_SIGNALS="unsafe" ./testloop06.pl
prejoin
thread: 1 
thread: 2 
thread: 3 
^Cthread: 4 
out
Segmentation fault

Perl :: Signals :: Unsafe:を使用する以下を追加しても結果は同じです。

$mthr = threads->create(\&counter);

UNSAFE_SIGNALS {
  $mthr->join();
};

ほとんどそこに、うまくいけば誰かがチャイムを鳴らすことができます... :)

4

1 に答える 1

5

シグナルハンドラは、Perlオペコード間でのみ呼び出されます。コードはでブロックされて$mthr->join();いるため、シグナルを処理することはできません。

考えられる解決策:

use Time::HiRes qw( sleep );

# Interruptable << $_->join() for threads->list; >>
while (threads->list()) {
   my @joinable = threads->list(threads::joinable);
   if (@joinable) {
      $_->join for @joinable;
   } else {
      sleep(0.050);
   }
}
于 2011-09-23T20:57:30.593 に答える