2012-01-28 5 views
5

के साथ ithreads का उपयोग करने में त्रुटि मैंने अभी एक पर्ल प्रोग्राम में धागे प्रस्तुत किए हैं, जहां इसके मॉड्यूल में से एक Memoize का उपयोग कर रहा था। मुझे यह त्रुटि संदेश मिल रहा है:मेमोइज़

थ्रेड 1 असामान्य रूप से समाप्त हो गया: बेनामी फ़ंक्शन वर्जित स्केलर संदर्भ में बुलाया गया; गलती

त्रुटि तब होती है जब मेरे पास धागे और यादें हों, लेकिन अगर मैं इन तत्वों में से किसी एक को हटा दूंगा तो गायब हो जाएगा। लेकिन समस्या यह नहीं है क्योंकि ज्ञापन थ्रेड-सुरक्षित नहीं है - मेरे कोड में, सभी ज्ञापन एक ही थ्रेड के भीतर होता है।

क्या यह ज्ञापन के साथ एक बग है? क्या मैं इस तरह से काम कर सकता हूं? अन्यथा मैं Memoize से छुटकारा पाने जा रहा हूँ।

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

आप किस संस्करण का पर्ल चल रहे हैं? ([यह बग] (https://rt.perl.org/rt3/Public/Bug/Display.html?id=79996) के कारण पूछ रहा है।) – Mat

+0

मैं स्ट्रॉबेरी पर्ल 5.12.3 का उपयोग Memoize 1.02 के साथ कर रहा हूं। मैं उस बग को पुन: पेश नहीं कर सका। – stevenl

उत्तर

4

याद रखें प्रत्येक ज्ञापन फ़ंक्शन के लिए एक हैश (बंद करने के बजाय) में कैश स्टोर करता है। यह उस हैश में इंडेक्स के रूप में फ़ंक्शन के पते का उपयोग करता है।

समस्या यह है कि फ़ंक्शन का पता तब बदलता है जब इसे किसी नए थ्रेड में क्लोन किया जाता है। (print(\&foo, "\n");invoke_foo में जोड़ें।)। यह Memoize में एक बग है।

वर्कअराउंड: थ्रेड्स के भीतर से ज्ञात मॉड्यूल लोड करें। निम्नलिखित अनुकरण (प्रासंगिक पहलुओं) कि:

use strict; 
use warnings; 
use threads; 
use Memoize; 

sub foo { 
    return shift; 
} 

sub invoke_foo { 
    return foo(@_); 
} 

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

वैसे, प्रत्येक धागे का अपना कैश होता है। जिसे एक बग भी माना जा सकता है।

+0

मैंने अभी 5 साल पहले (बग रिपोर्ट] (https://rt.cpan.org/Public/Bug/Display.html?id=21707) देखा है (अभी भी अनसुलझा) – stevenl

1

Memoize, धागे के तहत काम करना चाहिए थोड़ा यद्यपि धीमी:

"जिस तरह से गोटो के साथ कुछ समस्या है & च के तहत काम करता

यहाँ समस्या को अलग करने के कुछ नमूना कोड थ्रेडेड पर्ल, शायद @_ के लेक्सिकल स्कॉपिंग के कारण। यह पर्ल में एक बग है, और जब तक इसे हल नहीं किया जाता है, ज्ञापन कार्यों को थोड़ा अलग कॉलर() दिखाई देगा और थ्रेडपर थोड़ा और धीरे-धीरे प्रदर्शन करेगा असंबद्ध perls की तुलना मेंperls। "

2

जैसा कि उल्लेख किया गया है, Memoize धागा जागरूक नहीं है। यदि आप प्रति थ्रेड ज्ञापन चाहते हैं, तो ikegami का पुनर्गठन अच्छी तरह से काम करेगा। आप वैश्विक Memoization चाहते हैं, तो निम्नलिखित की तरह कुछ के साथ Memoize की जगह, तो इसके बजाय काम कर सकता था:

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) 
     } 
    } 
} 

और इसका इस्तेमाल करने के लिए:

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 
} 

जो प्रिंट:

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

memoize_shared समारोह उपर्युक्त जटिल है क्योंकि यह सूची और स्केलर संदर्भों के साथ-साथ नामित सबराउटिन को प्रतिस्थापित करने से संबंधित है।कभी-कभी यह आसान है बस लक्ष्य सबरूटीन में memoziation निर्माण करने के लिए:

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

सबरूटीन में Memoization बिल्डिंग यह थोड़ा और अधिक जटिल पड़ता है, लेकिन यह memoize की तरह एक आवरण समारोह का उपयोग कर से अधिक तेजी से हो जाएगा। और यह आपको threads::shared कैश का उपयोग करने जैसी चीज़ों सहित सबराउटिन को याद करने के तरीके पर सटीक नियंत्रण देता है।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^