2012-01-29 39 views
3

对于以下程序我得到此错误消息:可以将ithreads与Moose懒惰属性一起使用吗?

线程2异常终止:在 读者的Foo ::栏(在定义...第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

那么至少CPAN测试人员显示穆斯测试套件与螺纹波尔斯通过结果http://www.cpantesters.org/distro/M/Moose.html#Moose-2.0401虽然我不知道什么测试套件将执行线程。 – perigrin 2012-01-29 19:02:51

+0

Perl可能不会将'default'中的代码引用复制到新线程中。 – 2012-01-29 19:03:15

+0

@BradGilbert也许,虽然同样的错误也会发生,如果我更改默认''建设者',我不希望有coderef复制 – stevenl 2012-01-30 01:21:40

回答

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"; 
    } 
} 

注有在enqueuedequeue使用细微但重要的差异。最重要的是,当使用T :: Q :: A时,必须在列表上下文中调用dequeue。这是因为enqueue的参数列表作为一条消息传递,并且dequeue返回该参数列表。