少し変更されたボス/ワーカー モデルに基づいて、マルチスレッド アプリケーションを実装しようとしています。基本的に、メイン スレッドはいくつかのボス スレッドを作成し、それらはそれぞれ 2 つのワーカー スレッドを生成します (場合によってはそれ以上)。これは、ボス スレッドがそれぞれ 1 つのホストまたはネットワーク デバイスを処理し、ワーカー スレッドが作業を完了するのに時間がかかるためです。
私はThread::Pool
この概念を実現するために使用していますが、これまでのところ非常にうまく機能しています。また、私の問題が関連しているとは思いませんThread::Pool
(以下を参照)。非常に単純化された擬似コード:
use strict;
use warnings;
my $bosspool = create_bosspool(); # spawns all boss threads
my $taskpool = undef; # created in each boss thread at
# creation of each boss thread
# give device jobs to boss threads
while (1) {
foreach my $device ( @devices ) {
$bosspool->job($device);
}
sleep(1);
}
# This sub is called for jobs passed to the $bosspool
sub process_boss
{
my $device = shift;
foreach my $task ( $device->{tasks} ) {
# process results as they become available
process_result() while ( $taskpool->results );
# give task jobs to task threads
scalar $taskpool->job($device, $task);
sleep(1); ### HACK ###
}
# process remaining results / wait for all tasks to finish
process_result() while ( $taskpool->results || $taskpool->todo );
# happy result processing
}
sub process_result
{
my $result = $taskpool->result_any();
# mangle $result
}
# This sub is called for jobs passed to the $taskpool of each boss thread
sub process_task
{
# not so important stuff
return $result;
}
ところで、monitor()
-routine を使用しない理由は、すべてのジョブが終了するのを待たなければならないから$taskpool
です。さて、この### HACK ###
行を削除しない限り、このコードは素晴らしく機能します。スリープし$taskpool->todo()
ないと、ジョブを追加したり結果を受け取ったりするのが「速すぎる」場合、適切な数のまだ開いているジョブが提供されません。同様に、合計で 4 つのジョブを追加しますが、$taskpool->todo()
後で返されるのは 2 つだけです (保留中の結果はありません)。これにより、あらゆる種類の興味深い効果が得られます。
OK、それThread::Pool->todo()
はがらくたです。回避策を試してみましょう:
sub process_boss
{
my $device = shift;
my $todo = 0;
foreach my $task ( $device->{tasks} ) {
# process results as they become available
while ( $taskpool->results ) {
process_result();
$todo--;
}
# give task jobs to task threads
scalar $taskpool->job($device, $task);
$todo++;
}
# process remaining results / wait for all tasks to finish
while ( $todo ) {
process_result();
sleep(1); ### HACK ###
$todo--;
}
}
ラインを維持している限り、これも問題なく機能し### HACK ###
ます。Thread::Pool->todo()
この行がないと、このコードは の問題を再現します$todo
。 は 1 だけでなく 2 またはそれ以上減少します。
私はこのコードを 1 つのボス スレッドだけでテストしたので、(このサブルーチンに関しては) 基本的にマルチスレッドは関係ありませんでした。$bosspool
、$taskpool
そして特にそうで$todo
はありません:shared
、副作用はありえませんよね?このサブルーチンは、共有変数やセマフォなどを使用せずに 1 つのボス スレッドによってのみ実行されるので、何が起こっているのでしょうか?