php實現協程,真正的非同步
github上php的協程大部分是根據這篇文章實現的:http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html。
它們最終的結果都是把回調變成了優雅的順序執行的代碼,但還是阻塞的,不是真正的非同步。
比如最熱門的:https://github.com/recoilphp/recoil
先安裝:
composer require recoil/recoil
執行:
<?php
//recoil.php
include __DIR__ . "/vendor/autoload.php";
use RecoilReactReactKernel;
$i = 100000;
ReactKernel::start(task1());
ReactKernel::start(task2());
function task1(){
global $i;
echo "wait start" . PHP_EOL;
while ($i-- > 0) {
yield;
}
echo "wait end" . PHP_EOL;
};
function task2(){
echo "Hello " . PHP_EOL;
yield;
echo "world!" . PHP_EOL;
}
結果:
wait start
//等待若干秒
wait end
Hello
world!
我本來是想讓兩個任務並行,結果兩個任務變成了串列,中間等待的時間什麼事情都幹不了。React響應式的編程是嚴格禁止這種等待的,所以我就參照unity3d的協程自己寫了個php版本的。上代碼:
<?php
//Coroutine.php
//依賴swoole實現的定時器,也可以用其它方法實現定時器
class Coroutine
{
//可以根據需要更改定時器間隔,單位ms
const TICK_INTERVAL = 1;
private $routineList;
private $tickId = -1;
public function __construct()
{
$this->routineList = [];
}
public function start(Generator $routine)
{
$task = new Task($routine);
$this->routineList[] = $task;
$this->startTick();
}
public function stop(Generator $routine)
{
foreach ($this->routineList as $k => $task) {
if($task->getRoutine() == $routine){
unset($this->routineList[$k]);
}
}
}
private function startTick()
{
swoole_timer_tick(self::TICK_INTERVAL, function($timerId){
$this->tickId = $timerId;
$this->run();
});
}
private function stopTick()
{
if($this->tickId >= 0) {
swoole_timer_clear($this->tickId);
}
}
private function run()
{
if(empty($this->routineList)){
$this->stopTick();
return;
}
foreach ($this->routineList as $k => $task) {
$task->run();
if($task->isFinished()){
unset($this->routineList[$k]);
}
}
}
}
class Task
{
protected $stack;
protected $routine;
public function __construct(Generator $routine)
{
$this->routine = $routine;
$this->stack = new SplStack();
}
/**
* [run 協程調度]
* @return [type] [description]
*/
public function run()
{
$routine = &$this->routine;
try {
if(!$routine){
return;
}
$value = $routine->current();
//嵌套的協程
if ($value instanceof Generator) {
$this->stack->push($routine);
$routine = $value;
return;
}
//嵌套的協程返回
if(!$routine->valid() && !$this->stack->isEmpty()) {
$routine = $this->stack->pop();
}
$routine->next();
} catch (Exception $e) {
if ($this->stack->isEmpty()) {
/*
throw the exception
*/
return;
}
}
}
/**
* [isFinished 判斷該task是否完成]
* @return boolean [description]
*/
public function isFinished()
{
return $this->stack->isEmpty() && !$this->routine->valid();
}
public function getRoutine()
{
return $this->routine;
}
}
測試代碼:
<?php
//test.php
require "Coroutine.php";
$i = 10000;
$c = new Coroutine();
$c->start(task1());
$c->start(task2());
function task1(){
global $i;
echo "wait start" . PHP_EOL;
while ($i-- > 0) {
yield;
}
echo "wait end" . PHP_EOL;
};
function task2(){
echo "Hello " . PHP_EOL;
yield;
echo "world!" . PHP_EOL;
}
結果:
wait start
Hello
world!
//等待幾秒,但不阻塞
wait end
打開今日頭條,查看更多精彩圖片※使用Sinopia搭建私有的npm倉庫
※webSocket如何解決自動關閉的意思
TAG:程序員小新人學習 |