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 相关文章推荐
php添加文章时生成静态HTML文章的实现代码
Feb 17 PHP
thinkphp区间查询、统计查询与SQL直接查询实例分析
Nov 24 PHP
php中mt_rand()随机数函数用法
Nov 24 PHP
php递归删除目录与文件的方法
Jan 30 PHP
typecho插件编写教程(五):核心代码
May 28 PHP
php生成二维码
Aug 10 PHP
PHP图像裁剪缩略裁切类源码及使用方法
Jan 07 PHP
Yii列表定义与使用分页方法小结(3种方法)
Jul 15 PHP
CI框架(ajax分页,全选,反选,不选,批量删除)完整代码详解
Nov 01 PHP
thinkphp5.1 文件引入路径问题及注意事项
Jun 13 PHP
PHP安装memcache扩展的步骤讲解
Feb 14 PHP
PHP调用全国天气预报数据接口查询天气示例
Feb 20 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
高分R级DC动画剧《哈莉·奎茵》第二季正式预告首发
2020/04/09 欧美动漫
php数组函数序列之sort() 对数组的元素值进行升序排序
2011/11/02 PHP
PHP+FastCGI+Nginx配置PHP运行环境
2014/08/07 PHP
PHP获取当前页面URL函数实例
2014/10/22 PHP
PHP的Laravel框架中使用消息队列queue及异步队列的方法
2016/03/21 PHP
PHP命令Command模式用法实例分析
2018/08/08 PHP
PhpStorm的使用教程(本地运行PHP+远程开发+快捷键)
2020/03/26 PHP
深入浅析安装PhpStorm并激活的步骤详解
2020/09/17 PHP
Java 正则表达式学习总结和一些小例子
2012/09/13 Javascript
js 如何实现对数据库的增删改查
2012/11/23 Javascript
JQuery select(下拉框)操作方法汇总
2015/04/15 Javascript
jQuery unbind 删除绑定事件详解
2016/05/24 Javascript
15位和18位身份证JS校验的简单实例
2016/07/18 Javascript
详解js的六大数据类型
2016/12/27 Javascript
vue学习笔记之vue1.0和vue2.0的区别介绍
2017/05/17 Javascript
Node.js开发第三方微信公众平台
2017/06/05 Javascript
详解webpack + vue + node 打造单页面(入门篇)
2017/09/23 Javascript
jQuery+Cookie实现切换皮肤功能【附源码下载】
2018/03/25 jQuery
Jquery和CSS实现选择框重置按钮功能
2018/11/08 jQuery
layui实现三级导航菜单
2019/07/26 Javascript
js中apply和call的理解与使用方法
2019/11/27 Javascript
JavaScript实现轮播图效果
2020/10/30 Javascript
编写Python的web框架中的Model的教程
2015/04/29 Python
django 创建过滤器的实例详解
2017/08/14 Python
用python结合jieba和wordcloud实现词云效果
2017/09/05 Python
用Python生成HTML表格的方法示例
2020/03/06 Python
vscode+PyQt5安装详解步骤
2020/08/12 Python
python 使用三引号时容易犯的小错误
2020/10/21 Python
英国最大的网上药品商店:Chemist Direct
2017/12/16 全球购物
Watchshop德国:欧洲在线手表No.1
2019/06/20 全球购物
英国高级健康和美容产品零售商:Life and Looks
2019/08/01 全球购物
化工工艺专业求职信
2013/09/22 职场文书
幼师求职自荐信
2014/05/31 职场文书
我的中国梦心得体会范文
2016/01/05 职场文书
GoLang中生成UUID唯一标识的实现
2021/05/08 Golang
实现一个简单得数据响应系统
2021/11/11 Javascript