ithreads可以与Moose懒惰属性一起使用吗?

时间:2012-01-29 14:55:59

标签: multithreading perl moose

对于以下程序,我收到此错误消息:

  

线程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本身是线程安全的(我在文档中找不到相关内容)?

1 个答案:

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

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