我正在尝试基于略微改变的boss / worker模型实现多线程应用程序。基本上,主线程创建了几个boss线程,每个线程又产生两个工作线程(可能更多)。这是因为boss线程每个都处理一个主机或网络设备,而工作线程可能需要一段时间才能完成他们的工作。
我正在使用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()
例程的原因是因为我必须等待$taskpool
中的所有工作完成。现在,除非删除### HACK ###
行,否则此代码的效果非常好。如果您没有睡觉,$taskpool->todo()
将无法提供正确数量的作业,如果您添加它们或者“快速”收到它们的结果。比如,您总共添加了4个作业,但$taskpool->todo()
之后只返回2个作业(没有待处理的结果)。这会产生各种有趣的效果。
好的,所以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甚至更多。
我只用一个boss线程测试了这段代码,所以基本上没有涉及多线程(当涉及到这个子程序时)。 $bosspool
,$taskpool
,尤其是$todo
不是:shared
,没有副作用,对吗?在这个子程序中发生了什么,它只由一个boss线程执行,没有共享变量,信号量等等?
答案 0 :(得分:0)
我建议实现'worker'线程模型的最佳方法是使用Thread::Queue
。做这样的事情的问题是弄清楚队列何时完成,或者项目是否已经出队以及待处理。
使用Thread::Queue
,您可以使用while循环从队列中获取元素,并使用end
队列,以便while循环返回undef并退出线程。
因此,您并不总是需要多个“boss”线程,您可以使用多种不同版本的worker
和输入队列。我会问为什么你需要一个'boss'线程模型。这似乎没必要。
参考: Perl daemonize with child daemons
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue;
my $nthreads = 4;
my @targets = qw ( device1 device2 device3 device4 );
my $task_one_q = Thread::Queue->new();
my $task_two_q = Thread::Queue->new();
my $results_q = Thread::Queue->new();
sub task_one_worker {
while ( my $item = task_one_q->dequeue ) {
#do something with $item
$results_q->enqueue("$item task_one complete");
}
}
sub task_two_worker {
while ( my $item = task_two_q->dequeue ) {
#do something with $item
$results_q->enqueue("$item task_two complete");
}
}
#start threads;
for ( 1 .. $nthreads ) {
threads->create( \&task_one_worker );
threads->create( \&task_two_worker );
}
foreach my $target (@targets) {
$task_one_q->enqueue($target);
$task_two_q->enqueue($target);
}
$task_one_q->end;
$task_two_q->end;
#Wait for threads to exit.
foreach my $thr ( threads->list() ) {
threads->join();
}
$results_q->end();
while ( my $item = $results_q->dequeue() ) {
print $item, "\n";
}
如果您愿意,可以使用boss
线程执行类似的操作 - 您可以按boss
创建一个队列,并通过引用传递给工作人员。我不确定它是否必要。