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


Posted in PHP onJuly 17, 2019

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

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
COM in PHP (winows only)
Oct 09 PHP
全世界最小的php网页木马一枚 附PHP木马的防范方法
Oct 09 PHP
PHP 身份验证方面的函数
Oct 11 PHP
php 中英文语言转换类代码
Aug 11 PHP
PHP中shuffle数组值随便排序函数用法
Nov 21 PHP
PHP多文件上传实例
Jul 09 PHP
PHP邮件群发机实现代码
Feb 16 PHP
PHP strcmp()和strcasecmp()的区别实例
Nov 05 PHP
浅析php中array_map和array_walk的使用对比
Nov 20 PHP
php 解决扫描二维码下载跳转问题
Jan 13 PHP
PHP实现对图片的反色处理功能【测试可用】
Feb 01 PHP
php设计模式之中介者模式分析【星际争霸游戏案例】
Mar 23 PHP
php学习笔记之字符串常见操作总结
Jul 16 #PHP
thinkPHP+mysql+ajax实现的仿百度一下即时搜索效果详解
Jul 15 #PHP
[原创]PHP global全局变量经典应用与注意事项分析【附$GLOBALS用法对比】
Jul 12 #PHP
php array_chunk()函数用法与注意事项
Jul 12 #PHP
laravel框架中间件 except 和 only 的用法示例
Jul 12 #PHP
Laravel框架实现多数据库连接操作详解
Jul 12 #PHP
php遍历目录下文件并按修改时间排序操作示例
Jul 12 #PHP
You might like
PHP+MYSQL开发工具及资源收藏
2007/01/02 PHP
PHP 5.0对象模型深度探索之属性和方法
2008/03/27 PHP
php中如何同时使用session和cookie来保存用户登录信息
2013/07/05 PHP
PHP实现更新中间关联表数据的两种方法
2014/09/01 PHP
两种php给图片加水印的实现代码
2020/04/18 PHP
WordPress中登陆后关闭登陆页面及设置用户不可见栏目
2015/12/31 PHP
一份老外写的XMLHttpRequest代码多浏览器支持兼容性
2007/01/11 Javascript
js下弹出窗口的变通
2007/04/18 Javascript
JavaScript实现的石头剪刀布游戏源码分享
2014/08/22 Javascript
jQuery EasyUI实现右键菜单变灰不可用效果
2015/09/24 Javascript
深入解析桶排序算法及Node.js上JavaScript的代码实现
2016/07/06 Javascript
JS中如何实现点击a标签返回页面顶部的问题
2017/01/19 Javascript
bootstrap weebox 支持ajax的模态弹出框
2017/02/23 Javascript
vue中用动态组件实现选项卡切换效果
2017/03/25 Javascript
JavaScript html5 canvas实现图片上画超链接
2017/10/20 Javascript
基于element-ui封装可搜索的懒加载tree组件的实现
2020/05/22 Javascript
在react-antd中弹出层form内容传递给父组件的操作
2020/10/24 Javascript
[03:48]显微镜下的DOTA2第四期——TP动作
2014/06/20 DOTA
[00:55]2015国际邀请赛中国区预选赛5月23日——28日约战上海
2015/05/25 DOTA
Python中实现从目录中过滤出指定文件类型的文件
2015/02/02 Python
Python实现把数字转换成中文
2015/06/29 Python
python+selenium开发环境搭建图文教程
2017/08/11 Python
基于并发服务器几种实现方法(总结)
2017/12/29 Python
Python多进程multiprocessing.Pool类详解
2018/04/27 Python
Tornado Web Server框架编写简易Python服务器
2018/07/28 Python
Python绘制股票移动均线的实例
2019/08/24 Python
python保存log日志,实现用log日志画图
2019/12/24 Python
Python3 集合set入门基础
2020/02/10 Python
python zip,lambda,map函数代码实例
2020/04/04 Python
解决pytorch 保存模型遇到的问题
2021/03/03 Python
10个很棒的 CSS3 开发工具 推荐
2011/05/16 HTML / CSS
html5使用canvas画一条线
2014/12/15 HTML / CSS
HTML5+css3:3D旋转木马效果相册
2017/01/03 HTML / CSS
上下班时间调整通知
2015/04/23 职场文书
有关信念的名言语录集锦
2019/12/06 职场文书
选购到合适的激光打印机
2022/04/21 数码科技