当前位置:首页 > PHP教程 > php高级应用 > 列表

php基于协程实现异步的方法分析

发布:smiling 来源: PHP粉丝网  添加日期:2021-12-04 20:35:19 浏览: 评论:0 

本文实例讲述了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

执行:

  1. <?php 
  2. //recoil.php 
  3. include __DIR__ . '/vendor/autoload.php'; 
  4. use Recoil\React\ReactKernel; 
  5. $i = 100000; 
  6. ReactKernel::start(task1()); 
  7. ReactKernel::start(task2()); 
  8. function task1(){ 
  9.   global $i; 
  10.   echo "wait start" . PHP_EOL; 
  11.   while ($i-- > 0) { 
  12.     yield; 
  13.   } 
  14.   echo "wait end" . PHP_EOL; 
  15. }; 
  16. function task2(){ 
  17.   echo "Hello " . PHP_EOL; 
  18.   yield; 
  19.   echo "world!" . PHP_EOL; 
  20. } 

结果:

  1. wait start 
  2. //等待若干秒 
  3. wait end 
  4. Hello 
  5. world! 

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

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

测试代码:

  1. <?php 
  2. //test.php 
  3.  require 'Coroutine.php'; 
  4. $i = 10000; 
  5. $c = new Coroutine(); 
  6. $c->start(task1()); 
  7. $c->start(task2()); 
  8. function task1(){ 
  9.   global $i; 
  10.   echo "wait start" . PHP_EOL; 
  11.   while ($i-- > 0) { 
  12.     yield; 
  13.   } 
  14.   echo "wait end" . PHP_EOL; 
  15. }; 
  16. function task2(){ 
  17.   echo "Hello " . PHP_EOL; 
  18.   yield; 
  19.   echo "world!" . PHP_EOL; 
  20. } 

结果:

  1. wait start 
  2. Hello 
  3. world! 
  4. //等待几秒,但不阻塞 
  5. wait end

Tags: php协程 php异步

分享到: