Можно ли использовать ithreads с ленивыми атрибутами Moose? - PullRequest
3 голосов
/ 29 января 2012

Для приведенной ниже программы я получаю это сообщение об ошибке:

Поток 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 Ответ

2 голосов
/ 31 января 2012

Thread :: Queue заменяет все хэши и массивы в вашем объекте на общие хэши и массивы с одинаковым содержимым и делает это рекурсивно.

В то же время вы пытаетесь изменить объект.Да, это не закончится хорошо.(И не из-за какой-либо ошибки в Moose, Thread :: Queue или threads.)

Решение заключается в передаче объекта в сериализованной форме.Вы можете выполнить сериализацию и десериализацию самостоятельно или использовать 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.Самое главное, dequeue должен вызываться в контексте списка при использовании T :: Q :: A.Это связано с тем, что список аргументов enqueue передается как одно сообщение, а dequeue возвращает этот список аргументов.

...