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 Recoil\React\ReactKernel;  
  
$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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM