首頁  >  文章  >  後端開發  >  php中關於協程與阻塞的深入理解

php中關於協程與阻塞的深入理解

黄舟
黄舟原創
2017-08-11 11:37:001654瀏覽

這篇文章主要給大家介紹了關於PHP中協程和阻塞的一些理解與思考,文中透過範例程式碼介紹的非常詳細,對大家學習或使用PHP具有一定的參考學習價值,需要的朋友們下面跟著小編來一起學習學習吧。

前言

本文主要介紹了PHP中協程與阻塞的理解與思考,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹:

進程、執行緒、協程

##關於進程、線程、協程,有非常詳細和豐富的博客或學習資源,我不在此做贅述,我大致在此介紹一下這幾個東西。

  • 進程擁有自己獨立的堆和棧,既不共享堆,也不共享棧,進程由作業系統調度。

  • 線程擁有自己獨立的棧和共享的堆,共享堆,不共享棧,線程也由操作系統調度(標準線程是的)。

  • 協程和執行緒一樣共享堆,不共享棧,協程由程式設計師在協程的程式碼裡顯示調度。

PHP中的協程實作基礎yield

yield的根本實作是生成器類,而迭代器類別是迭代器介面的實作:


Generator implements Iterator {
 public mixed current ( void ) // 返回当前产生的值
 public mixed key ( void ) // 返回当前产生的键
 public void next ( void ) // 生成器继续执行
 public void rewind ( void ) // 重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
   // renwind的执行将会导致第一个yield被执行, 并且忽略了他的返回值.
 public mixed send ( mixed $value ) // 向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。如果当这个方法被调用时,生成器 
   // 不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。
 public void throw ( Exception $exception ) // 向生成器中抛入一个异常
 public bool valid ( void ) // 检查迭代器是否被关闭
 public void __wakeup ( void ) // 序列化回调,抛出一个异常以表示生成器不能被序列化。
}

我就以他實作的協程多任務調度為基礎做一下範例說明並說一下關於我在阻塞方面所做的一些思考。

自訂簡單定時執行任務範例:

#(此範例必須依賴上述鳥哥實作的協程調度程式碼)


class timer {
 private $start = 0; // 定时开始时间
 private $timer; // 间隔的时间差,单位秒
 private $value = 0; // 产生的结果值
 private $callback; // 异步回调
 private $isEnd = false; // 当前定时器任务是否结束
 public function __construct($timer,callable $callback)
 {
 $this->start = time();
 $this->timer = $timer;
 $this->callback = $callback;
 }
 public function run() {
 if($this->valid()) {
 $callback = $this->callback;
 $callback($this->value ++,$this);
 $this->start = time();
 }
 }
 /**
 * 定时执行检查
 */
 public function valid() {
 $end = time();
 if($end - $this->start >= $this->timer) {
 return true;
 } else {
 return false;
 }
 }
 public function setEnd($isEnd) {
 $this->isEnd = $isEnd;
 }
 public function getEnd() {
 return $this->isEnd;
 }
}

/**
 * 模拟阻塞的协程1
 *
 */
function taskObject1() {
 $timer = new timer(1,function($value,timer $timer) {
 if($value >= 5) {
 $timer->setEnd(true);
 }
 echo &#39;<br>&#39;.&#39;A &#39;.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
/**
 * 模拟阻塞的协程2
 *
 */
function taskObject2() {
 $timer = new timer(2,function($value,timer $timer) {
 if($value >= 3) {
 $timer->setEnd(true);
 }
 echo &#39;<br>&#39;.&#39;B &#39;.$value;
 });
 $tid = (yield getTaskId());
 while (true) {
 if($timer->getEnd() == true) {
 break;
 }
 yield $timer->run();
 }
}
$scheduler = new Scheduler;
$scheduler->newTask(taskObject1());
$scheduler->newTask(taskObject2());
$scheduler->run();

以上實現的是:

  • #產生兩個任務,並行執行,並且給每個任務在執行的時候模擬幾秒鐘的阻塞;

  • 讓協程切換的時候能順利切換,其中的任務阻塞不互相影響;

思考:

我為什麼要做以上這件事?因為我發現協程實作雖然很強大也很有意思,能讓多任務並行,但是我在其中一個任務裡調用系統函數

sleep() 的時候,阻塞任務會阻止協程切換,其實從協程的實現原理上來書也是這麼回事。

那麼,我也就想模擬協程阻塞,但是不產生阻塞看是否可行。 PHP本身只提供了生成器為協程呼叫提供了支撐,如果不依賴擴展,沒有提供多執行緒的程式實作方式,沒有java那麼強大,可以開子執行緒進行實作。

我印像中java的子線程是獨立執行且不會相互阻塞的,所以我在想,PHP既然可以實現類似於多線程這樣的機制,那麼能不能實現調用過程中非阻塞呢?

經過這樣一個實現和思考,一開始是陷入了一個誤區的,是由於PHP原生函數

sleep() 阻塞造成的思維誤區,那就是認為要想真正實現非阻塞或說實現非同步的話,是必須依賴語言底層的。

後來,我想明白了一個道理,既然某個方法或函數在執行過程中,會產生阻塞,那麼把目前這個方法換成自訂的,就做成非阻塞(相對於整個協程調度來說)不就行了嗎?例如上面的定時執行我自己實作了一個。

而另一方面,協程調度本身的目的也是為了把任務執行過程切成盡量小片,以便快速切換執行,達到並行的目的。從這方面來看,協程應該也算是一種程式設計想法。

以下是一個程式切成盡量小片執行的例子:


#

// 一个简单的例子
<?php
function xrange($start, $end, $step = 1) {
 for ($i = $start; $i <= $end; $i += $step) {
 yield $i;
 }
}
 
foreach (xrange(1, 1000000) as $num) {
 echo $num, "\n";
}

這個例子是把原本用range產生一個很大的整數陣列的方式切換為分片執行,也就是說在遍歷的時候再去取到指定的值,從程式碼來看,記憶體消耗相對於之前來說就非常小了。

總結#

以上是php中關於協程與阻塞的深入理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn