您的位置:首页 > 编程语言 > PHP开发

php实现协程,真正的异步

2016-06-15 18:43 716 查看

php实现协程,真正的异步

2016-06-15 18:43
1454人阅读 评论(1)
收藏
举报


分类:
php(5)


版权声明:本文为博主原创文章,未经博主允许不得转载。

github上php的协程大部分是根据这篇文章实现的:http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html。

它们最终的结果都是把回调变成了优雅的顺序执行的代码,但还是阻塞的,不是真正的异步。

比如最热门的:https://github.com/recoilphp/recoil

先安装:

[html]
view plain
copy





composer require recoil/recoil  

执行:

[php]
view plain
copy





<?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]
view plain
copy





<?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]
view plain
copy





<?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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: