2015-09-01 6 views
5

मैं साझा ऑब्जेक्ट का उपयोग करने के लिए एक सुरक्षित और तेज़ तरीका खोज रहा हूं।PHP pthreads - साझा वस्तुओं

मैंने पहले से ही प्रश्न पूछा: https://github.com/krakjoe/pthreads/issues/470 लेकिन obviuously यह सही जगह नहीं थी।

कई अन्य संदर्भों (थ्रेड) के साथ ऑब्जेक्ट (थ्रेडेड) साझा करने का प्रयास कर रहा है। सभी धागे इस शर्ड ऑब्जेक्ट को अपडेट कर रहे हैं - वे अपने अनुरोध सेट कर सकते हैं और दूसरों से अनुरोधों का जवाब भी दे सकते हैं।

अब क्रैकोजो ने जवाब दिया कि लॉक/अनलॉक 7 में उपलब्ध नहीं होगा मुझे कोई समस्या है।

मुझे पता है: सिंक्रनाइज़ किया गया है, लेकिन मुझे यह नहीं पता कि यह मेरी आवश्यकताओं के लिए काम करने के लिए इसका उपयोग कैसे करें।

मैं कैसे उपयोग कर सकते हैं :: तरह

  • ताला()
  • अनलॉक() पद्धतियों लिखने के लिए सिंक्रनाइज़
  • is_locked() - जाँच करता है, तो बंद कर दिया, और यदि हां न कोशिश - बस जाओ पर और बाद में

संपादित करें का प्रयास करें:

मैं एक (IMO) बहुत आसान परीक्षण पटकथा लिखी।

इस स्क्रिप्ट में कोई syc/lock/... विधियां एटीएम शामिल हैं।

यह दिखाना चाहिए कि मैं क्या करने की कोशिश कर रहा हूं।

अभी भी यह साझा करने के लिए एक तरीका खोज रहा है :: यह साझा सुरक्षित बनाने के लिए।

कोड:

<?php 
/* 
TEST: 
    create n threads 
    each will 
     - Shared::set() its own ref 
     - check if Shared::exists() its own ref 
     - Shared::get() its ref back 
     - call method ::isRunning() at returned val to easily check if is ref or got overwritten by another context 

TODO: 
    using ::synchronized to handle multi-context-access 

NOTES: 
    every method as public to prevent pthreads v2 "Method Modifiers - Special Behaviour" 
     see: "Method Modifiers - Special Behaviour" 
      at http://blog.krakjoe.ninja/2015/08/a-letter-from-future.html 
*/ 
class Shared extends Threaded 
{ 
    public $data; 
    public function exists($ident) 
    { 
     return isset($this->data[$ident]); 
    } 
    public function set($ident, $ref) 
    { 
     $return = false; 
     if(!isset($this->data[$ident])){ 
      $data = $this->data; 
      $data[$ident] = $ref; 
      $this->data = $data; 
      $return = $this->data[$ident]; 
     } 
     #echo __METHOD__ . '(' . $ident . ') => ' . gettype($return) . PHP_EOL; 
     return $return; 
    } 
    public function get($ident) 
    { 
     $return = false; 
     if($this->exists($ident) === true){ 
      $data = $this->data; 
      $return = $data[$ident]; 
      unset($data[$ident]); 
      $this->data = $data; 
     } 
     #echo __METHOD__ . '(' . $ident . ') => ' . gettype($return) . PHP_EOL; 
     return $return; 
    } 
} 

class T extends Thread 
{ 
    public $count; 
    public function __construct(Shared $Shared, $ident) 
    { 
     $this->Shared = $Shared; 
     $this->ident = $ident; 
    } 
    public function run() 
    { 
     $slowdown = true; 
     $this->count = 0; 
     while(true){ 
      if($slowdown){ 
       // "don't allow usleep or sleep" : https://github.com/krakjoe/pthreads/commit/a157b34057b0f584b4db326f30961b5c760dead8 
       // loop a bit to simulate work: 
       $start = microtime(true); 
       $until = rand(1, 100000)/1000000; 
       while(microtime(true)-$start < $until){ 
        // ... 
       } 
      } 

      if($this->Shared->exists($this->ident) === true){ 
       $ref = $this->Shared->get($this->ident); 
      } 
      else{ 
       $ref = $this->Shared->set($this->ident, $this); 
      } 
      // calling a method on $ref -- if not a ref we crash 
      $ref->isRunning(); 
      unset($ref); 
      $this->count++; 
     } 
    } 
} 


echo 'start ...' . PHP_EOL; 

$n = 8; 
$Shared = new Shared(); 
for($i = 0, $refs = array(); $i < $n; $i++){ 
    $refs[$i] = new T($Shared, $i); 
    $refs[$i]->start(); 
} 

while(!empty($refs)){ 
    // print status: 
    if(!isset($t)or microtime(true)-$t > 1){ 
     $t = microtime(true); 
     echo 'status: ' . count($refs) . ' running atm ...' . PHP_EOL; 
    } 

    // join crashed threads: 
    foreach($refs as $i => $thread){ 
     if($thread->isRunning() === false){ 
      echo 'T-' . $i . ' stopped after ' . $thread->count . PHP_EOL; 
      if($thread->isJoined() === false){ 
       $thread->join(); 
      } 
      unset($refs[$i]); 
     } 
    } 
} 

echo 'no thread running anymore.' . PHP_EOL; 

/* output 
start ... 
status: 8 running atm ... 

Notice: Undefined offset: 6 in ...\shared_test.php on line 33 

Fatal error: Call to a member function isRunning() on null in ...\shared_test.php on line 82 
T-6 stopped after 10 
status: 7 running atm ... 

Notice: Undefined offset: 4 in ...\shared_test.php on line 33 

Fatal error: Call to a member function isRunning() on null in ...\shared_test.php on line 82 
T-4 stopped after 35 
status: 6 running atm ... 

Notice: Undefined offset: 7 in ...\shared_test.php on line 33 

Fatal error: Call to a member function isRunning() on null in ...\shared_test.php on line 82 
T-7 stopped after 43 
status: 5 running atm ... 
status: 5 running atm ... 
status: 5 running atm ... 

[...] 
*/ 
?> 
+0

यह एक अच्छा सवाल है – r3wt

उत्तर

8

Threaded वस्तुओं पहले से ही सुरक्षित थ्रेड कर रहे हैं, कि है कि कहने के लिए, किसी भी समय आप पढ़ते हैं, लिखते हैं, के होने की जाँच, या एक सदस्य (सेट नहीं) को हटा है, ऑपरेशन है परमाणु - कोई दूसरा संदर्भ पहले ऑपरेशन के दौरान उपर्युक्त संचालन में से कोई भी प्रदर्शन नहीं कर सकता है। इंजन हैंडलरों के लिए यह भी सच है कि उपयोगकर्ता को अनजान है, सब कुछ निम्नतम स्तर तक नीचे है।

काफी फिर से आश्वस्त है, तथापि ... यह स्पष्ट सीमा जब तर्क इस तरह की स्थापना या इसके साथ कुछ और कर के रूप में आप कर रहे हैं इससे पहले कि एक सदस्य के अस्तित्व की जाँच के रूप में, और अधिक जटिल हो जाता है है: पर कार्रवाई करते हुए ऑब्जेक्ट परमाणु है, unset को किसी अन्य संदर्भ को isset पर कॉल करने और संपत्ति/आयाम को पढ़ने के लिए कॉल करने के लिए कुछ भी नहीं है।

यह PHP7 पर लागू होता है (pthreads v3 +)

सुरक्षा, और अखंडता दो अलग बातें यहाँ हैं। जब अखंडता महत्वपूर्ण होती है तो आप इसे सही ढंग से संरक्षित करने के लिए PHP7 में Threaded::synchronized का उपयोग कर सकते हैं। PHP5 में आप इसे भी संरक्षित कर सकते हैं, लेकिन स्पष्टीकरण के रूप में कोड अधिक जटिल होगा।

आपका दूसरा उदाहरण अनिश्चित काल तक चलना चाहिए, अगर मैं इसे तर्क समझता हूं।तो मैं सही कोड बनाने के लिए उस धारणा का उपयोग कर रहा हूं, मैं इस अंतहीन पाश में क्या करना चाहूंगा और कुछ अंतर्दृष्टि प्रदान करता हूं जहां यह आवश्यक लगता है।

<?php 
class Referee extends Threaded { 

    public function find(string $ident, Threaded $reference) { 
     return $this->synchronized(function() use($ident, $reference) { 
      if (isset($this[$ident])) { 
       return $this[$ident]; 
      } else return ($this[$ident] = $reference); 
     }); 
    } 

    public function foreach(Closure $closure) { 
     $this->synchronized(function() use($closure) { 
      foreach ($this as $ident => $reference) { 
       $closure($ident, $reference); 
      } 
     }); 
    } 
} 

class Test extends Thread { 

    public function __construct(Referee $referee, string $ident, bool $delay) { 
     $this->referee = $referee; 
     $this->ident = $ident; 
     $this->delay = $delay; 
    } 

    public function run() { 
     while (1) { 
      if ($this->delay) { 
       $this->synchronized(function(){ 
        $this->wait(1000000); 
       }); 
      } 

      $reference = 
       $this->referee->find($this->ident, $this); 

      /* do something with reference here, I guess */   

      /* do something with all references here */ 
      $this->referee->foreach(function($ident, $reference){ 
       var_dump(Thread::getCurrentThreadId(), 
         $reference->getIdent(), 
         $reference->isRunning()); 
      }); 
     } 
    } 

    public function getIdent() { 
     return $this->ident; 
    } 

    private $referee; 
    private $ident; 
    private $delay; 
} 

$referee = new Referee(); 
$threads = []; 
$thread = 0; 
$idents = [ 
    "smelly", 
    "dopey", 
    "bashful", 
    "grumpy", 
    "sneezy", 
    "sleepy", 
    "happy", 
    "naughty" 
]; 

while ($thread < 8) { 
    $threads[$thread] = new Test($referee, $idents[$thread], rand(0, 1)); 
    $threads[$thread]->start(); 
    $thread++; 
} 

foreach ($threads as $thread) 
    $thread->join(); 
?> 

तो हम मतभेदों को देखेंगे, मैं आपको बता देंगे कि क्यों वे के रूप में वे कर रहे हैं और और कैसे आप उन्हें लिख सकते हैं, तो आप पहले से ही पता है कि हम सुरक्षा के बारे में अब बात नहीं कर रहे हैं, लेकिन ईमानदारी , आपको समझाया गया है कि (काफी उल्लेखनीय) धारणा है कि जो भी आप लिखते हैं वह "सुरक्षित" है, जैसा कि समझाया गया है।

if ($this->delay) { 
    $this->synchronized(function(){ 
     $this->wait(1000000); 
    }); 
} 

यह केवल, आप Thread ही उपयोग करने के लिए सिंक्रनाइज़ करने के लिए नहीं होगा एक Thread प्रतीक्षा करने के लिए एक उपयुक्त तरीका है, आप किसी भी Threaded वस्तु इस्तेमाल कर सकते हैं:

पहला बड़ा अंतर है, यह है । चीजें ठीक से कर रही है, के मामले में के लाभ नहीं स्पष्ट कर दिया कि, नींद और usleep का उपयोग कर ::wait करता है एक ग्रहणशील राज्य में धागे मत छोड़ो, है।

असली दुनिया है, जहां आप वास्तव में ही कभी कुछ के लिए इंतजार करना चाहिए में, कि एक अधिक जटिल ब्लॉक, यह हो सकता है हो सकता है (और चाहिए) और अधिक की तरह लग रहे:

if ($this->delay) { 
    $this->synchronized(function(){ 
     while ($this->condition) { 
      $this->wait(1000000); 
     } 
    }); 
} 

नोट: इंतज़ार एक टाइमआउट के लिए तकनीकी रूप से कुछ इंतजार कर रहा है, हालांकि, आप टाइमआउट के अलावा किसी अन्य चीज़ से जागृत हो सकते हैं, और इसके लिए कोड तैयार किया जाना चाहिए।

इस तरह की है कि, एक और संदर्भ Thread कि यह इंतजार कर और शट डाउन शान से बंद कर देना चाहिए सूचित कर दें या बाहर कुछ अन्य महत्वपूर्ण कार्रवाई तुरंत ले जाने के लिए, बस, सिंक्रनाइज़ किया जा रहा एक शर्त बदल रहा है और Thread को सूचित करके करने में सक्षम है।

उम्मीद के मुताबिक कोड के लिए, यह कैसे सिंक्रनाइज़, रुको और काम को सूचित के साथ आराम पाने के लिए अत्यंत महत्वपूर्ण है।

अगला हम संदर्भ की स्थापना और या प्राप्त करने के लिए हमारे तर्क है:

$reference = 
    $this->referee->find($this->ident, $this); 

कौन सा invokes इस:

public function find(string $ident, Threaded $reference) { 
    return $this->synchronized(function() use($ident, $reference) { 
     if (isset($this[$ident])) { 
      return $this[$ident]; 
     } else return ($this[$ident] = $reference); 
    }); 
} 

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

मैं आप उस विशेष संदर्भ (जो हमेशा वर्तमान में $this होने जा रहा है) के साथ कुछ करना लगता है। मैं अनुमान नहीं लगा सकता कि क्या। आगे बढ़ते ...

मैं इस धारणा है कि आप इन Threads से प्रत्येक के साथ कुछ करना चाहता हूँ कि कर दिया है, और आप डेटा की अखंडता की रक्षा करने के लिए है, जबकि पूरे यात्रा जगह लेता हैं:

$this->referee->foreach(function($ident, $reference){ 
    var_dump(Thread::getCurrentThreadId(), 
      $reference->getIdent(), 
      $reference->isRunning()); 
}); 

कौन सा आह्वान:

public function foreach(Closure $closure) { 
    $this->synchronized(function() use($closure) { 
     foreach ($this as $ident => $reference) { 
      $closure($ident, $reference); 
     } 
    }); 
} 

इस तरह आप ऐसा होता है।

यह उल्लेखनीय है कि सिंक्रनाइज़ करने की आवश्यकता यहां आवश्यक नहीं है; जैसे कि कुछ भी बुरा नहीं होगा यदि आप किसी सरणी से किसी सदस्य को हटाते हैं, तो आप पुनरावृत्ति करते समय कुछ भी बुरा नहीं होगा, अगर आप किसी ऑब्जेक्ट को अनसेट या सेट या करते हैं तो पुनरावृत्ति होती है।

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