php实现协程,真正的异步

来源:互联网 发布:吉林破获特大网络诈骗 编辑:程序博客网 时间:2024/05/29 06:51

php实现协程,真正的异步

1454人阅读 评论(1)收藏举报
分类:

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 在CODE上查看代码片派生到我的代码片
  1. composer require recoil/recoil  

执行:

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?php  
  2. //recoil.php  
  3.   
  4. include __DIR__ . '/vendor/autoload.php';  
  5.   
  6. use Recoil\React\ReactKernel;  
  7.   
  8. $i = 100000;  
  9.   
  10. ReactKernel::start(task1());  
  11. ReactKernel::start(task2());  
  12.   
  13. function task1(){  
  14.     global $i;  
  15.     echo "wait start" . PHP_EOL;  
  16.     while ($i-- > 0) {  
  17.         yield;  
  18.     }  
  19.     echo "wait end" . PHP_EOL;  
  20. };  
  21.   
  22. function task2(){  
  23.     echo "Hello " . PHP_EOL;  
  24.     yield;  
  25.     echo "world!" . PHP_EOL;  
  26. }  

结果:

wait start
//等待若干秒
wait end
Hello
world!

我本来是想让两个任务并行,结果两个任务变成了串行,中间等待的时间什么事情都干不了。React响应式的编程是严格禁止这种等待的,所以我就参照unity3d的协程自己写了个php版本的。上代码:

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?php  
  2. //Coroutine.php  
  3. //依赖swoole实现的定时器,也可以用其它方法实现定时器  
  4.   
  5. class Coroutine  
  6. {  
  7.     //可以根据需要更改定时器间隔,单位ms  
  8.     const TICK_INTERVAL = 1;  
  9.   
  10.     private $routineList;  
  11.   
  12.     private $tickId = -1;  
  13.   
  14.     public function __construct()  
  15.     {  
  16.         $this->routineList = [];  
  17.     }  
  18.   
  19.     public function start(Generator $routine)  
  20.     {  
  21.         $task = new Task($routine);  
  22.         $this->routineList[] = $task;  
  23.         $this->startTick();  
  24.     }  
  25.   
  26.     public function stop(Generator $routine)  
  27.     {  
  28.         foreach ($this->routineList as $k => $task) {  
  29.             if($task->getRoutine() == $routine){  
  30.                 unset($this->routineList[$k]);  
  31.             }  
  32.         }  
  33.     }  
  34.   
  35.     private function startTick()  
  36.     {  
  37.         swoole_timer_tick(self::TICK_INTERVAL, function($timerId){  
  38.             $this->tickId = $timerId;  
  39.             $this->run();  
  40.         });  
  41.     }  
  42.   
  43.     private function stopTick()  
  44.     {  
  45.         if($this->tickId >= 0) {  
  46.             swoole_timer_clear($this->tickId);  
  47.         }  
  48.     }  
  49.   
  50.     private function run()  
  51.     {  
  52.         if(empty($this->routineList)){  
  53.             $this->stopTick();  
  54.             return;  
  55.         }  
  56.   
  57.         foreach ($this->routineList as $k => $task) {  
  58.             $task->run();  
  59.   
  60.             if($task->isFinished()){  
  61.                 unset($this->routineList[$k]);  
  62.             }  
  63.         }  
  64.     }  
  65.       
  66. }  
  67.   
  68. class Task  
  69. {  
  70.     protected $stack;  
  71.     protected $routine;  
  72.   
  73.     public function __construct(Generator $routine)  
  74.     {  
  75.         $this->routine = $routine;  
  76.         $this->stack = new SplStack();  
  77.     }  
  78.   
  79.     /** 
  80.      * [run 协程调度] 
  81.      * @return [type]         [description] 
  82.      */  
  83.     public function run()  
  84.     {  
  85.         $routine = &$this->routine;  
  86.   
  87.         try {  
  88.   
  89.             if(!$routine){  
  90.                 return;  
  91.             }  
  92.   
  93.             $value = $routine->current();   
  94.   
  95.             //嵌套的协程  
  96.             if ($value instanceof Generator) {  
  97.                 $this->stack->push($routine);  
  98.                 $routine = $value;  
  99.                 return;  
  100.             }  
  101.   
  102.             //嵌套的协程返回  
  103.             if(!$routine->valid() && !$this->stack->isEmpty()) {  
  104.                 $routine = $this->stack->pop();  
  105.             }  
  106.   
  107.             $routine->next();  
  108.   
  109.         } catch (Exception $e) {  
  110.   
  111.             if ($this->stack->isEmpty()) {  
  112.                 /* 
  113.                     throw the exception  
  114.                 */  
  115.                 return;  
  116.             }  
  117.         }  
  118.     }  
  119.   
  120.     /** 
  121.      * [isFinished 判断该task是否完成] 
  122.      * @return boolean [description] 
  123.      */  
  124.     public function isFinished()  
  125.     {  
  126.         return $this->stack->isEmpty() && !$this->routine->valid();  
  127.     }  
  128.   
  129.     public function getRoutine()  
  130.     {  
  131.         return $this->routine;  
  132.     }  
  133. }  

测试代码:

[php] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <?php  
  2. //test.php  
  3.   
  4.  require 'Coroutine.php';  
  5.   
  6. $i = 10000;  
  7.   
  8. $c = new Coroutine();  
  9. $c->start(task1());  
  10. $c->start(task2());  
  11.   
  12. function task1(){  
  13.     global $i;  
  14.     echo "wait start" . PHP_EOL;  
  15.     while ($i-- > 0) {  
  16.         yield;  
  17.     }  
  18.     echo "wait end" . PHP_EOL;  
  19. };  
  20.   
  21. function task2(){  
  22.     echo "Hello " . PHP_EOL;  
  23.     yield;  
  24.     echo "world!" . PHP_EOL;  
  25. }  

结果:

wait start
Hello
world!
//等待几秒,但不阻塞
wait end

0 0
原创粉丝点击