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开发留言板的CRUD(增,删,改,查)操作
Apr 19 PHP
php多文件上传功能实现原理及代码
Apr 18 PHP
解析php函数method_exists()与is_callable()的区别
Jun 21 PHP
PHP基础知识介绍
Sep 17 PHP
PHP中批量生成静态html(命令行下运行PHP)
Apr 19 PHP
php模拟登陆的实现方法分析
Jan 09 PHP
全面解读PHP的Yii框架中的日志功能
Mar 17 PHP
php htmlentities()函数的定义和用法
May 13 PHP
Yii2如何批量添加数据
May 17 PHP
PHP实现表单提交数据的验证处理功能【防SQL注入和XSS攻击等】
Jul 21 PHP
laravel5.1 ajax post 传值_token示例
Oct 24 PHP
解析laravel使用workerman用户交互、服务器交互
Apr 28 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防止sql注入示例分析和几种常见攻击正则表达式
2014/01/12 PHP
php解析xml方法实例详解
2015/05/12 PHP
Mootools 1.2教程(2) DOM选择器
2009/09/14 Javascript
3Z版基于jquery的图片复选框(asp.net+jquery)
2010/04/12 Javascript
Javascript 函数parseInt()转换时出现bug问题
2014/05/20 Javascript
node.js中的fs.fstatSync方法使用说明
2014/12/15 Javascript
JavaScript每天定时更换皮肤样式的方法
2015/07/01 Javascript
HTML5实现留言和回复页面样式
2015/07/22 Javascript
JavaScript获取当前运行脚本文件所在目录的方法
2016/02/03 Javascript
Swiper 4.x 使用方法(移动端网站的内容触摸滑动)
2018/05/17 Javascript
jQuery实现table表格信息的展开和缩小功能示例
2018/07/21 jQuery
详解如何模拟实现node中的Events模块(通俗易懂版)
2019/04/15 Javascript
vue3修改link标签默认icon无效问题详解
2019/10/09 Javascript
jQuery实现消息弹出框效果
2019/12/10 jQuery
原生jQuery实现只显示年份下拉框
2020/12/24 jQuery
举例讲解Python程序与系统shell交互的方式
2015/04/09 Python
Python使用defaultdict读取文件各列的方法
2017/05/11 Python
Python3实现发送QQ邮件功能(文本)
2017/12/15 Python
python3读取excel文件只提取某些行某些列的值方法
2018/07/10 Python
Python判断以什么结尾以什么开头的实例
2018/10/27 Python
python 遍历列表提取下标和值的实例
2018/12/25 Python
简单了解django缓存方式及配置
2019/07/19 Python
Python在cmd上打印彩色文字实现过程详解
2019/08/07 Python
win10系统下python3安装及pip换源和使用教程
2020/01/06 Python
Python hashlib常见摘要算法详解
2020/01/13 Python
python实现随机加减法生成器
2020/02/24 Python
日本最新流行服饰网购:Nissen
2016/07/24 全球购物
美国眼镜网站:EyeBuyDirect
2017/04/13 全球购物
运动服饰每月订阅盒:Ellie
2018/04/29 全球购物
网络安全方面的面试题
2016/01/07 面试题
学院书画协会部门岗位职责
2013/12/01 职场文书
担保书格式
2015/01/20 职场文书
励志语录:时光飞逝,请学会珍惜所有的人和事
2020/01/16 职场文书
QT连接MYSQL数据库的详细步骤
2021/07/07 MySQL
python DataFrame中stack()方法、unstack()方法和pivot()方法浅析
2022/04/06 Python
使用CSS实现六边形的图片效果
2022/08/05 HTML / CSS