PHP協程初體驗

2021-06-27 05:31:28 字數 4571 閱讀 9585

by warezhou 2014.11.24

上次通過c擴充套件為php新增coroutine嘗試失敗之後,由於短期內啃下zend可能性幾乎為零,只能打語言原生能力的主意了。google之後發現,php5.5引入了generator和coroutine新特性,於是才有了本文的誕生。

《當c/c++後台開發遇上coroutine》

《一次失敗的php擴充套件開發之旅》

function my_range($start, $end, $step = 1) 

}foreach (my_range(1, 1000) as $num)

/* * 1

* 2* ...

* 1000

*/

圖 1 基於generator的range()實現

$range = my_range(1, 1000);

var_dump($range);

/* * object(generator)#1 (0)

*/var_dump($range instanceof iterator);

/* * bool(true)

*/

圖 2 my_range()的實現推測

由於接觸php時日尚淺,並未深入語言實現細節,所以只能根據現象進行猜測,以下是我的一些個人理解:

細心的讀者可能已經發現,截至目前,其實generator已經實現了coroutine的關鍵特性:中斷執行、恢復執行。按照《當c/c++後台開發遇上coroutine》的思路,借助「全域性變數」一類語言設施進行資訊傳遞,實現非同步server應該足夠了。

其實相對於swapcontext族函式,generator已經前進了一大步,具備了「返回資料」的能力,如果同時具備「傳送資料」的能力,就再也不必通過那些蹩腳的手法繞路而行了。在php裡面,通過generator的send()介面(注意:不再是next()介面),可以完成「傳送資料」的任務,從而實現了真正的「雙向通訊」。

function gen() 

$gen = gen();

$ret = $gen->current();

echo "[main]", $ret, "\n";

$ret = $gen->send("send1");

echo "[main]", $ret, "\n";

$ret = $gen->send("send2");

echo "[main]", $ret, "\n";

/* * [main]yield1

* [gen]send1

* [main]yield2

* [gen]send2

* [main]

*/

圖 3 coroutine雙向通訊示例

作為c/c++系碼農,發現「可重入」、「雙向通訊」能力之後,貌似沒有更多奢求了,不過php還是比較慷慨,繼續新增了exception機制,「錯誤處理」機制得到進一步完善。

function gen()  catch (exception $ex)    

echo "[gen]finish\n";

}$gen = gen();

$ret = $gen->current();

echo "[main]", $ret, "\n";

$ret = $gen->send("send1");

echo "[main]", $ret, "\n";

$ret = $gen->throw(new exception("test"));

echo "[main]", $ret, "\n";

/* * [main]yield1

* [gen]send1

* [main]yield2

* [gen][exception]test

* [gen]finish

* [main]

*/

圖 4 coroutine錯誤處理示例

前面簡單介紹了相關的語言設施,那麼具體到實際專案中,到底應該如何運用呢?讓我們繼續《一次失敗的php擴充套件開發之旅》描述的場景,借助上述特性實現那個美好的願望:以同步方式書寫非同步**

<?php 

class asyncserver

if (!socket_set_nonblock($this->socket))

if(!socket_bind($this->socket, "0.0.0.0", 1234))

}public function run()

$writes = null;

$excepts= null;

if (!socket_select($reads, $writes, $excepts, 0, 1000))

foreach ($reads as $one)

if ($one == $this->socket)

if (!socket_set_nonblock($socket))

socket_sendto($socket, $task->data, $task->len, 0, $task->ip, $task->port);

$this->tasks[$socket] = [$socket, $coroutine];

} else else }}

}}}class asynctask

}function requesthandler($socket, $req_buf, $req_len, $ip, $port)

$server = new asyncserver(requesthandler);

$server->run();

?>

**解讀:

第一版遺留問題:

<?php 

class asyncserver

if (!socket_set_nonblock($this->socket))

if(!socket_bind($this->socket, "0.0.0.0", 1234))

}public function run()

unset($this->timers[$time]);

}$reads = array($this->socket);

foreach ($this->tasks as list($socket))

$writes = null;

$excepts= null;

if (!socket_select($reads, $writes, $excepts, 0, 1000))

foreach ($reads as $one)

if ($one == $this->socket)

$task = $coroutine->current();

//echo "[run]asynctask recv. data=$task->data ip=$task->ip port=$task->port timeout=$task->timeout\n";

$socket = socket_create(af_inet, sock_dgram, sol_udp);

if(!$socket)

if (!socket_set_nonblock($socket))

socket_sendto($socket, $task->data, $task->len, 0, $task->ip, $task->port);

$deadline = $now + $task->timeout;

$this->tasks[$socket] = [$socket, $coroutine, $deadline];

$this->timers[$deadline][$socket] = $socket;

} else }}

}}class asynctask

}function asyncsendrecv($req_buf, $req_len, $ip, $port, $timeout)

function requesthandler($socket, $req_buf, $req_len, $ip, $port) catch (exception $ex)

//echo "[requesthandler] after yield asynctask. rsp=$rsp_buf\n";

socket_sendto($socket, $rsp_buf, $rsp_len, 0, $ip, $port);

}$server = new asyncserver(requesthandler);

$server->run();

?>

**解讀:

攜程小程式初體驗

隨著小程式的大熱,作為乙個程式猿,我也開始接觸並且大概了解了乙個製作小程式的一些過程,為了提高自己的動手能力,於是乎,我開始來仿寫攜程的小程式,來實現一些基本功能,在仿寫的過程中,也遇到了一些難題,也有了一點收穫,希望可以通過這篇文章與大家共同交流,共同進步。為了更好的開發,我們需要準備我們需要的工...

php 協程理解

生成器生成器最基本的思想也是乙個函式,這個函式的返回值是依次輸出,而不是只返回乙個單獨的值。或者,換句話說,生成器使你更方便的實現了迭代器介面。下面通過實現乙個xrange函式來簡單說明 function xrange start,end,step 1 foreach xrange 1,100000...

協程巢狀協程

import asyncio import functools 第三層協程 async def test1 print 我是test1 await asyncio.sleep 1 print test1已經睡了1秒 await asyncio.sleep 3 print test1又睡了3秒 ret...