2012-01-29 78 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返回該參數列表。