2012-01-28 36 views
5

Tôi vừa giới thiệu các chủ đề cho một chương trình Perl, trong đó một trong các mô đun của nó đang sử dụng Memoize. Tôi nhận được thông báo lỗi này:Lỗi khi sử dụng ithreads với Memoize

Chủ đề 1 chấm dứt bất thường: Hàm ẩn danh được gọi trong ngữ cảnh vô hướng bị cấm; đứt gãy

Lỗi này xảy ra nếu tôi có cả hai chủ đề và Memoize, nhưng sẽ biến mất nếu tôi mất đi một trong những yếu tố này. Nhưng vấn đề không phải là vì Memoize không phải là chủ đề an toàn - trong mã của tôi, tất cả các ghi nhớ xảy ra trong cùng một chuỗi.

Đây có phải là lỗi với Bản ghi nhớ không? Có cách nào tôi có thể giải quyết vấn đề này không? Nếu không, tôi sẽ loại bỏ Memoize.

Dưới đây là một số mẫu mã để cô lập các vấn đề:

use strict; 
use warnings; 
use threads; 
use Thread::Semaphore; 
use Memoize; 

my $semaphore = Thread::Semaphore->new; 

memoize('foo'); 
sub foo { 
    return shift; 
} 

sub invoke_foo { 
    $semaphore->down; # ensure memoization is thread-safe 
    my $result = foo(@_); 
    $semaphore->up; 

    return $result; 
} 

my @threads; 
foreach (1 .. 5) { 
    my $t = threads->create(sub { invoke_foo($_) }); 
    push @threads, $t; 
} 
$_->join foreach @threads; 
+2

Bạn đang chạy phiên bản perl nào? (Hỏi vì [lỗi này] (https://rt.perl.org/rt3/Public/Bug/Display.html?id=79996).) – Mat

+0

Tôi đang sử dụng Strawberry Perl 5.12.3 với Memoize 1.02. Tôi không thể tái tạo lỗi đó. – stevenl

Trả lời

4

Ghi nhớ lưu trữ bộ đệm cho mọi chức năng ghi nhớ trong một lần băm (thay vì sử dụng đóng). Nó sử dụng địa chỉ của hàm làm chỉ mục vào băm đó.

Vấn đề là địa chỉ của hàm thay đổi khi nó được nhân bản thành một chuỗi mới. (Thêm print(\&foo, "\n"); vào invoke_foo.). Đó là một lỗi trong Memoize.

Cách khắc phục: Tải mô-đun ghi nhớ từ trong chuỗi. các mô phỏng sau đây (các khía cạnh liên quan của) rằng:

Nhân tiện, mỗi luồng có bộ nhớ cache riêng. đó cũng có thể được coi là một lỗi.

+0

Tôi vừa thấy [báo cáo lỗi] này (https://rt.cpan.org/Public/Bug/Display.html?id=21707) từ 5 năm trước (vẫn chưa được giải quyết) – stevenl

1

Memoize nên làm việc theo chủ đề, mặc dù chậm hơn một chút:

"Có một số vấn đề với cách goto & f hoạt động dưới Perl, có lẽ vì phạm vi từ vựng của @_ Đây là lỗi trong Perl, và cho đến khi nó được giải quyết, các chức năng ghi nhớ sẽ thấy một số người gọi khác nhau ( ) và sẽ thực hiện chậm hơn một chút trên luồng perls so với perls chưa được đọc. "

2

Như đã lưu ý, Memoize không phải là chủ đề. Nếu bạn muốn ghi nhớ mỗi luồng, việc tái cơ cấu của ikegami sẽ hoạt động tốt. Nếu thay vào đó bạn muốn memoization toàn cầu, sau đó thay thế Memoize với một cái gì đó như sau có thể làm việc:

use strict; 
use warnings; 
use 5.010; 
use threads; 
use threads::shared; 

sub memoize_shared { 
    my $name = shift; 
    my $glob = do { 
     no strict 'refs'; 
     \*{(caller)."::$name"} 
    }; 
    my $code = \&$glob; 
    my $sep = $;; 
    my (%scalar, %list) :shared; 

    no warnings 'redefine'; 
    *$glob = sub { 
     my $arg = join $sep => @_; 
     if (wantarray) { 
      @{$list{$arg} ||= sub {\@_}->(&$code)} 
     } 
     else { 
      exists $scalar{$arg} 
       ? $scalar{$arg} 
       :($scalar{$arg} = &$code) 
     } 
    } 
} 

và sử dụng nó:

sub foo { 
    my $x = shift; 
    say "foo called with '$x'"; 
    "foo($x)" 
} 

memoize_shared 'foo'; 

for my $t (1 .. 4) { 
    threads->create(sub { 
     my $x = foo 'bar'; 
     say "thread $t got $x" 
    })->join 
} 

mà in:

 
foo called with 'bar' 
thread 1 got foo(bar) 
thread 2 got foo(bar) 
thread 3 got foo(bar) 
thread 4 got foo(bar) 

Các memoize_shared chức năng ở trên là khá phức tạp bởi vì nó đề với danh sách propegating và ngữ cảnh vô hướng cũng như thay thế các chương trình con được đặt tên.Đó là đôi khi dễ dàng hơn để chỉ cần xây dựng các memoziation vào chương trình con mục tiêu:

{my %cache :shared; 
sub foo { 
    my $x = shift; 
    if (exists $cache{$x}) {$cache{$x}} 
    else { 
     say "foo called with '$x'"; 
     $cache{$x} = "foo($x)" 
    } 
}} 

Xây dựng memoization vào chương trình con không làm cho nó phức tạp hơn một chút, nhưng nó sẽ nhanh hơn so với sử dụng một hàm wrapper như memoize. Và nó cho phép bạn kiểm soát chính xác cách ghi nhớ chương trình con, bao gồm những thứ như sử dụng bộ nhớ cache threads::shared.

Các vấn đề liên quan