对于以下程序,我收到此错误消息:
线程2异常终止:共享标量的值无效 读者Foo :: bar(在第9行定义)第10行。
该程序由一个管道组成,其中第一个线程创建一些基于Moose的对象并将它们放入队列中,然后在第二个线程中拾取它们。问题似乎是该属性是懒惰的,因为如果我删除了延迟设置,错误就会消失。
package Foo;
use Moose;
has 'bar' => (
is => 'ro',
isa => 'HashRef', # the error doesn't happen with simpler datatypes
lazy => 1, # this line causes the error
default => sub { return { map {$_ => $_} (1 .. 10) } },
);
package main;
use threads;
use Thread::Queue;
my $threadq = Thread::Queue->new;
sub create {
# $_ doesn't seem to be thread-safe
# this resolved another problem I had with a custom Moose type constraint
# where the 'where' clause used $_
local $_;
$threadq->enqueue( Foo->new ) foreach 1 .. 5;
$threadq->enqueue( undef );
return;
}
sub process {
local $_;
while (my $f = $threadq->dequeue) {
print keys %{$f->bar}, "\n";
}
return;
}
threads->create( \&create )->join;
threads->create( \&process )->join;
有人能解释一下这个问题吗? Moose本身是线程安全的(我在文档中找不到相关内容)?
答案 0 :(得分:2)
Thread :: Queue用共享的哈希和具有相同内容的数组替换对象中的所有哈希和数组,并以递归方式执行。
同时,您正在尝试更改对象。是的,那不会结束。 (而不是因为Moose,Thread :: Queue或线程中的任何错误。)
解决方案是以序列化形式传递对象。您可以自己处理序列化和反序列化,也可以使用Thread::Queue::Any隐式完成序列化。
use threads;
use Thread::Queue::Any;
my $threadq = Thread::Queue::Any->new;
sub create {
$threadq->enqueue( Foo->new ) for 1 .. 5;
$threadq->enqueue( );
}
sub process {
while ( my ($f) = $threadq->dequeue ) {
print sort keys %{$f->bar};
print "\n";
}
}
请注意enqueue
和dequeue
的使用存在细微但重要的差异。最重要的是,在使用T :: Q :: A时,必须在列表上下文中调用dequeue
。这是因为enqueue
的参数列表作为一条消息传递,dequeue
返回该参数列表。