當前位置:
首頁 > 知識 > php實現協程,真正的非同步

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

php實現協程,真正的非同步

打開今日頭條,查看更多精彩圖片
喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

使用Sinopia搭建私有的npm倉庫
webSocket如何解決自動關閉的意思

TAG:程序員小新人學習 |