2013-11-25 109 views
4

如何在主要上下文中中斷線程的執行?PHP - 中斷或暫停pthread的執行

在下面的代碼片段中 - 如何停止線程在不破壞它的情況下執行的操作?

class ReadFileThread extends Thread 
{ 
    public function __construct($file, $chunk = 1024) 
    { 
     $this->file = $file; 

     $this->chunk = $chunk; 
    } 

    public function run() 
    { 
     if(is_file($this->file) && is_readable($this->file)) 
     { 
      $fh = fopen($this->file, 'rb'); 

      while(!feof($fh)) 
      { 
       $content = fread($fh, $this->chunk); 
      } 

      fclose($fh); 
     } 
    } 
} 

$num = 10; 
$threads = []; 

for($i = 0; $i < $num; $i++) 
{ 
    $thread = new ReadFileThread('/path/to/10gig_file.txt', 1024); 

    $threads[] = $thread; 

    // I start the thread, now it's detached from the main context and is reading the file asynchronously 
    $thread->start(); 
} 

// The interesting part - I want to random 1 thread whose operation of file reading I want to interrupt 
$to_interrupt = $threads[rand(0, $num)]; 

// How to interrupt the thread without destroying it? I want its context preserved 

回答

9

RandomSeeds答案是接近,但開放的競爭條件。

<?php 
class FileReader extends Thread { 
    public $file; 
    public $pause; 

    public function __construct($file) { 
     $this->file = $file; 
     $this->pause = false; 
    } 

    public function run() { 
     if (($handle = fopen($this->file, "rb"))) { 
      $len = 0; 
      do { 
       $this->synchronized(function(){ 
        if ($this->paused) { 
         printf(
          "\npausing %lu ...\n", $this->getThreadId()); 
         $this->wait(); 
        } 
       }); 

       $data = fread($handle, 1024); 
       $len += strlen($data); 

       if (($len % 2) == 0) { 
        printf( 
         "\r\rread %lu", $len); 
       } 
      } while (!feof($handle)); 

      fclose($handle); 
     } 
    } 

    public function pause() { 
     return $this->synchronized(function(){ 
      return ($this->paused = true); 
     }); 
    } 

    public function unpause() { 
     return $this->synchronized(function(){ 
      $this->paused = false;  
      if ($this->isWaiting()) { 
       return $this->notify(); 
      } 
     }); 
    } 
} 

function do_something($time) { 
    $start = time(); 

    while (($now = time()) < ($start + $time)) { 
     usleep(100); 
     if (($now % 2) == 0) { 
      echo "."; 
     } 
    } 

    echo "\n"; 
} 

$reader = new FileReader("/path/to/big/file.ext"); 
$reader->start(); 

sleep(2); 
$reader->pause(); 
do_something(rand(2, 4)); 
$reader->unpause(); 
sleep(2); 
$reader->pause(); 
do_something(rand(2, 4)); 
$reader->unpause(); 
sleep(2); 
$reader->pause(); 
do_something(rand(2, 4)); 
$reader->unpause(); 
?> 

是用於同步的目的變量只能用在synchronized塊訪問是非常重要的,我已經省略了停止執行/退出功能,但它的邏輯是大致相同的,因爲RandomSeeds例所示。

競爭條件潛伏中:

public function mine($data) { 
    /* anyone can set doSynchronization at any time */ 
    if ($this->doSynchronization) { 

     $this->synchronize(function(){ 
      /* checking the predicate in here is safer */ 
      $this->wait(); 
     }); 
    } 
} 

好:

public function mine($data) { 
    $this->synchronize(function(){ 
     if ($this->doSynchronization) { 
      $this->wait(); 
     } 
    }); 
} 

極端:

public function mine($data) { 
    $this->synchronize(function(){ 
     while ($this->doSynchronization) { 
      $this->wait(); 
     } 
    }); 
} 

POSIX標準總是有你寫的極端方式,我沒有那麼臃腫,無論對你有用。這個極端代碼的原因是,必須允許線程接收除正在等待的信號之外的信號,許多低級信號可能會導致線程從調用pthread_cond_wait中喚醒;在循環中檢查謂詞,防止規範稱爲虛假喚醒......但這種極端措施也會導致不良副作用;那些線程收到低電平信號的原因是因爲它們需要採取某種行動,忽略這一點很容易導致堆棧的不同部分發生死鎖,因爲它預期線程死掉(或者做其他事情,模具是一個例子),當它發出信號...

+0

總是一種享受:)進一步閱讀:https://gist.github.com/krakjoe/6437782(如果你還沒有看到它) –

0

從來沒有使用pthreads,tbh,但你有沒有嘗試在線程類中做一個公共布爾標誌?

+0

我有,但是我不知道如何能夠幫助我打斷線程內發生的fopen/fread。你能否詳細說明一下? – Furicane

+0

我假設改變標誌是從已經運行的「run」方法中可見的。如果這個標誌會變成「false」,我們稱它爲「readTheFile」,那麼你只需跳過「fread」循環,直到標誌變回「true」。這樣做可以讓你更好地控制這個線程中發生的事情;你甚至可以獲得當前位置(ftell),存儲它,關閉文件句柄。主循環將繼續運行,等待「readTheFile」更改。如果它的「真」再次打開文件並繼續閱讀你離開的地方。 –

2

AFAIK您不能任意暫停併發線程,但可以向其發送通知。另一個線程在收到通知時必須合作並自願暫停。

例子:

<?php 

class MyThread extends Thread { 

    private $pauseRequested = false; 
    private $stopRequested = false; 

    public function pause() { 
     $this->synchronized(function($thread){ 
      $thread->pauseRequested = true; 
     }, $this); 
    } 

    public function resume() { 
     $this->synchronized(function($thread){ 
      $thread->pauseRequested = false; 
      $thread->notify(); 
     }, $this); 

    } 

    public function stop() { 
     $this->synchronized(function($thread){ 
      $thread->stopRequested = true; 
     }, $this); 
    } 

    public function run() { 
     echo 'Thread started!' . PHP_EOL; 
     while (!$this->stopRequested) { 

      // do the actual work 
      echo 'Working...'; 
      sleep(1); 

      // check if we have been requested to pause 
      $this->synchronized(function($thread){ 
       if ($this->pauseRequested) { 
        echo 'Paused...'; 
        $thread->wait(); // this is where the magic happens 
       } 
      }, $this); 
     } 

     if ($this->stopRequested) { 
      echo PHP_EOL . 'Stopped!' . PHP_EOL; 
     } 
    } 
} 


$t = new MyThread(); 
$t->start(); 
sleep(5); 

$t->pause(); 
sleep(2); 

$t->resume(); 
sleep(5); 

$t->stop(); 

// wait for $t to complete 
$t->join(); 

?> 
+0

@JoeWatkins謝謝你的解釋,我修復了我的代碼,以防萬一有人想重用它。我不是100%確定我的一切都正確,你是否介意證明我的編輯?注意我沒有同步對'$ this-> stopRequested'的訪問。既然國旗不能解除,我認爲它是安全的,不是嗎? – RandomSeed

+0

哦,並遵循相同的邏輯,是否可以*不*同步'MyThread :: stop()'? – RandomSeed

+0

謝謝你的回答,它非常有幫助。然而,喬的例子完美地爲我工作,因此無法接受這兩個答案 - 我在這一個upvoting你:) – Furicane