2012-05-07 10 views
7

मेरे पर्ल स्क्रिप्ट एक साथ कई धागे को चलाने के लिए की जरूरत है ...पर्ल में सेमफोर थ्रेड संचार को कैसे कार्यान्वित करें?

use threads ('yield', 'exit' => 'threads_only'); 
use threads::shared; 
use strict; 
use warnings; 
no warnings 'threads'; 
use LWP::UserAgent; 
use HTTP::Request; 
use HTTP::Async; 
use ... 

... और इस तरह के धागे वेब से कुछ जानकारी प्राप्त करने की आवश्यकता है, तो HTTP::Async प्रयोग किया जाता है।

my $request = HTTP::Request->new; 
    $request->protocol('HTTP/1.1'); 
    $request->method('GET'); 
    $request->header('User-Agent' => '...'); 

my $async = HTTP::Async->new(slots   => 100, 
           timeout   => REQUEST_TIMEOUT, 
           max_request_time => REQUEST_TIMEOUT); 

लेकिन कुछ धागे वेब तक पहुँचने के लिए केवल जब अन्य धागा (रों) तो कहते हैं की जरूरत है।

my $start = [Time::HiRes::gettimeofday()]; 
my @threads =(); 
foreach ... { 
    $thread = threads->create(
    sub { 
      local $SIG{KILL} = sub { threads->exit }; 
      my $url = shift; 
      if ($url ...) { 
      # wait for "go" signal from other threads 
      } 
      my ($response, $data); 
      $request->url($url); 
      $data = ''; 
      $async->add($request); 
      while ($response = $async->wait_for_next_response) { 
      threads->yield(); 
      $data .= $response->as_string; 
      } 
      if ($data ...) { 
      # send "go" signal to waiting threads 
      } 
     } 
     }, $_); 

    if (defined $thread) { 
    $thread->detach; 
    push (@threads, $thread); 
    } 
} 

"go" संकेत के लिए इंतजार कर एक या अधिक धागे हो सकती है और वहाँ एक या अधिक धागे कि इस तरह के "go" संकेत भेज सकते हैं हो सकता है। शुरुआत में सेमफोर की स्थिति " प्रतीक्षा करें" और एक बार यह " पर जाएं", यह ऐसा ही रहेगा।

अंत में, ऐप अधिकतम चलने का समय जांचता है। यदि धागे बहुत लंबे समय तक चल रहे हैं, तो आत्म-समाप्ति संकेत भेजा जाता है।

my $running; 
do { 
    $running = 0; 
    foreach my $thread (@threads) { 
    $running++ if $thread->is_running(); 
    } 
    threads->yield(); 
} until (($running == 0) || 
     (Time::HiRes::tv_interval($start) > MAX_RUN_TIME)); 
$running = 0; 
foreach my $thread (@threads) { 
    if ($thread->is_running()) { 
    $thread->kill('KILL'); 
    $running++; 
    } 
} 
threads->yield(); 

अब बात करने के लिए। मेरे प्रश्न हैं:

  1. कैसे कर सकता है मैं सबसे अधिक प्रभावी ढंग कोड "सेमाफोर" इंतज़ार कर लिपि में (ऊपर स्क्रिप्ट में टिप्पणी देखें)। क्या मुझे बस कुछ डमीsleepलूप के साथ साझा साझा चर का उपयोग करना चाहिए?

  2. मैं अनुप्रयोग के अंत में कुछsleepपाश जोड़ने के लिए विनाश के लिए धागे को समय देने की जरूरत है?

+0

मैं सही ढंग से समझते हैं कि आप अलग HTTP :: Async वस्तुओं (कॉपी, नहीं, नए सूत्र द्वारा साझा) लाने के लिए ज़्यादा से ज़्यादा एक यूआरएल का उपयोग कर रहे करें प्रति थ्रेड पर एक समय में? – pilcrow

+0

@pilcrow - हाँ, ऐसा लगता है। क्या यह संसाधनों का अपशिष्ट है? –

+0

यह कम स्मृति या समय-कुशल नहीं हो सकता है, लेकिन यह वास्तविक नाली प्रोग्रामर चक्रों पर है। :) डिजाइन को समझना मुश्किल है, और शायद सुरक्षित रूप से बदलने/विस्तार करने के लिए, क्योंकि घटक काफी सही नहीं लगते हैं। – pilcrow

उत्तर

3

आप Thread::Queue पर देखने के इस काम को करने के सकता है। आप एक कतार स्थापित कर सकते हैं जो 'जाने' सिग्नल और 'जाने' संकेत भेजने वाले धागे के बीच थ्रेड के बीच सिग्नलिंग को संभालेगा। यहां एक त्वरित नकली है कि मैं परीक्षण नहीं किया है:

... 
use Thread::Queue; 
... 
# In main body 
my $q = Thread::Queue->new(); 
... 
$thread = threads->create(
    sub { 
      local $SIG{KILL} = sub { threads->exit }; 
      my $url = shift; 
      if ($url ...) { 
      # wait for "go" signal from other threads 
      my $mesg = $q->dequeue(); 
      # you could put in some termination code if the $mesg isn't 'go' 
      if ($mesg ne 'go') { ... } 
      } 
      ... 
      if ($data ...) { 
      # send "go" signal to waiting threads 
      $q->enqueue('go'); 
      } 
     } 
     }, $_); 
... 

धागे एक 'जाना' सिग्नल के लिए प्रतीक्षा करने तक कुछ कतार में प्रवेश करती है विपंक्ति पद्धति पर इंतजार करेंगे की आवश्यकता है। एक बार जब संदेश कतार में एक थ्रेड में प्रवेश करता है और केवल एक धागा संदेश को पकड़ लेगा और इसे संसाधित करेगा।

यदि आप धागे को रोकना चाहते हैं ताकि वे नहीं चलें, तो आप कतार के सिर पर एक स्टॉप संदेश डाल सकते हैं।

$q->insert(0, 'stop') foreach (@threads); 

:: कतार और threads CPAN वितरण कि और अधिक विस्तार में यह दिखाने थ्रेड में उदाहरण हैं।

आपके दूसरे प्रश्न के जवाब में, उत्तर दुर्भाग्य से, यह निर्भर करता है। जब आप अपने धागे को समाप्त करने के लिए आगे बढ़ते हैं, तो क्लीन शटडाउन के लिए किस प्रकार की सफाई की आवश्यकता होती है? थ्रेड के नीचे से गलीचा गया था अगर सबसे खराब स्थिति परिदृश्य क्या हो सकता है? आप सफाई के लिए किसी भी समय योजना बनाना चाहते हैं। दूसरा विकल्प जो आप कर सकते हैं वह वास्तव में पूरा करने के लिए प्रत्येक थ्रेड पर प्रतीक्षा कर रहा है।

मेरी टिप्पणी का कारण यह पूछने का कारण है कि क्या आप detach कॉल को हटा सकते हैं क्योंकि यह विधि मुख्य धागे से बाहर निकलने की अनुमति देती है और किसी भी बच्चे के थ्रेड के साथ क्या हो रहा है परवाह नहीं करता है। इसके बजाय, अगर आप इस कॉल निकाल सकते हैं और जोड़ें:

$_->join() foreach threads->list(); 

आपका मुख्य ब्लॉक के अंत में, यह मुख्य आवेदन वास्तव में पूरा करने के लिए प्रत्येक थ्रेड के लिए प्रतीक्षा करने की आवश्यकता होगी।

यदि आप detach विधि को स्थान पर छोड़ देते हैं, तो आपको अपने कोड के अंत में सोना होगा यदि आपको अपने धागे को किसी भी तरह का क्लीन-अप करने की आवश्यकता है। जब आप थ्रेड पर detach पर कॉल करते हैं, तो आप पर्ल को क्या कह रहे हैं कि आप परवाह नहीं है कि थ्रेड क्या कर रहा है जब आपका मुख्य थ्रेड निकलता है। यदि मुख्य धागा निकलता है और ऐसे थ्रेड होते हैं जो अभी भी चल रहे हैं तो अलग हो गए हैं, तो प्रोग्राम कोई चेतावनी नहीं देगा। हालांकि, अगर आपको किसी भी क्लीन-अप की आवश्यकता नहीं है, और आप अभी भी detach पर कॉल करते हैं, तो जब चाहें बाहर निकलने के लिए स्वतंत्र महसूस करें।

+0

इस प्रश्न में +50 प्रतिष्ठा के लायक एक खुला बक्षीस है। कृपया ** अपना उत्तर सुधारें **। मैंने आपकी पोस्ट को दिलचस्प पाया, हालांकि आपने मेरे पोस्टिंग में दूसरे उप-प्रश्न (यदि/कैसे ** ** धागे स्वयं-विनाश के लिए ** प्रतीक्षा करें **) –

+0

@ user1215106 मैं आपके कोड में नोटिस करता हूं कि आप ' $ thread-> अलग; '। आमतौर पर आप थ्रेड को अनदेखा करने के लिए इसका उपयोग करते हैं और यदि यह पूरा हो जाता है या नहीं तो चिंतित नहीं है। क्या आपके पास वहां होने का कोई कारण है, या इसे हटाया जा सकता है? – Joel

+0

मेरा मानना ​​है कि इसे हटाया जा सकता है –

-1

कुछ इस तरह की कोशिश करें ....

#!/usr/bin/perl 

use threads; 
use threads::shared; 

$|=1; 

my ($global):shared; 
my (@threads); 

push(@threads, threads->new(\&mySub,1)); 
push(@threads, threads->new(\&mySub,2)); 
push(@threads, threads->new(\&mySub,3)); 

$i = 0; 

foreach my $myThread(@threads) 

{ 
    my @ReturnData = $myTread->join ; 
    print "Thread $i returned: @ReturnData\n"; 
    $i++; 
} 

sub mySub 
{ 
    my ($threadID) = @_; 

    for(0..1000) 
    { 
     $global++; 
     print "Thread ID: $threadID >> $_ >> GLB: $global\n"; 
     sleep(1); 
    } 
    return($id); 
} 
संबंधित मुद्दे