我有一个脚本可以创建一个队列,还有一些正在从队列中读取作业的工作人员。我现在的问题是脚本没有终止并调用printData(),因为线程是空闲的。这是因为我没有将队列设置为undef。
我尝试了很多不同的方法,但都会导致各种问题。
我使用以下代码
# -------------------------
# Main
# -------------------------
my @threads = map threads->create(\&doOperation), 1 .. $maxNumberOfParallelJobs;
pullDataFromDbWithDirectory($directory);
#$worker->enqueue((undef) x $maxNumberOfParallelJobs);
$_->join for @threads;
sub pullDataFromDbWithDirectory {
my $_dir = $_[0];
if ($itemCount <= $maxNumberOfItems) {
my @retval = grep { /^Dir|^File/ } qx($omnidb -filesystem $filesystem '$label' -listdir '$_dir');
foreach my $item (@retval) {
$itemCount++;
(my $filename = $item) =~ s/^File\s+|^Dir\s+|\n//g;
my $file = "$_dir/$filename";
push(@data,$file);
if ($item =~ /^Dir/) {
$worker->enqueue($file);
print "Add $file to queue\n" if $debug;
}
}
}
}
sub doOperation () {
my $ithread = threads->tid();
do {
my $folder = $worker->dequeue();
print "Read $folder from queue with thread $ithread\n" if $debug;
pullDataFromDbWithDirectory($folder);
} while ($worker->pending());
push(@IDLE_THREADS,$ithread);
}
编辑:
我找到了一个丑陋的解决方案。也许有更好的?我将工人添加到IDLE数组中并一直睡到所有工人都在那里
sleep 0.01 while (scalar @IDLE_THREADS < $maxNumberOfParallelJobs);
$worker->enqueue((undef) x $maxNumberOfParallelJobs);
$_->join for @threads;
答案 0 :(得分:2)
您不能在没有线程过早死亡的情况下使用->pending()
。修正:
my $busy: shared = $num_workers;
sub pullDataFromDbWithDirectory {
my $tid = threads->tid();
while (defined( my $folder = $q->dequeue() )) {
{ lock $busy; ++$busy; }
print "Worker thread $tid processing folder $folder.\n" if $debug;
pullDataFromDbWithDirectory($folder);
{ lock $busy; --$busy; }
}
print "Worker thread $tid exiting.\n" if $debug;
}
sleep 0.01 while $q->pending || $busy;
$worker->end();
$_->join for @threads;
但这引入了竞争条件。
出列加上繁忙的递增需要是原子的,挂起的检查加上繁忙的检查需要是原子的。
如果不改变Thread :: Queue,那是不可能做到的。你不能只是锁定这两段代码,因为这会阻止主机在其中一个空闲时检查所有线程是否空闲。
我们需要将->dequeue
拆分为等待组件及其出列组件。我们有后者(->dequeue_nb
),所以我们只需要前者。
use Thread::Queue 3.01;
sub T_Q_wait {
my $self = shift;
lock(%$self);
my $queue = $$self{'queue'};
my $count = @_ ? $self->_validate_count(shift) : 1;
# Wait for requisite number of items
cond_wait(%$self) while ((@$queue < $count) && ! $$self{'ENDED'});
cond_signal(%$self) if (@$queue);
return !$$self{'ENDED'};
}
现在我们可以编写解决方案了:
my $busy: shared = 0;
sub pullDataFromDbWithDirectory {
my $tid = threads->tid();
WORKER_LOOP:
while (T_Q_wait($q)) {
my $folder;
{
lock $busy;
$folder = $q->dequeue_nb();
next WORKER_LOOP if !defined($folder);
++$busy;
}
print "Worker thread $tid processing folder $folder.\n" if $debug;
pullDataFromDbWithDirectory($folder);
{
lock $busy;
--$busy;
cond_signal($busy) if !$busy;
}
}
}
{
lock $busy;
cond_wait($busy) while $busy;
$q->end();
$_->join() for threads->list();
}
如果另一个帖子在next
和wait
之间阻碍了工作,则会dequeue_nb
。