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 相关文章推荐
apache+mysql+php+ssl服务器之完全安装攻略
Sep 05 PHP
PHP 5.0对象模型深度探索之绑定
Sep 05 PHP
php heredoc和phpwind的模板技术使用方法小结
Mar 28 PHP
php中session_unset与session_destroy的区别分析
Jun 16 PHP
设置php页面编码的两种方法示例介绍
Mar 03 PHP
Codeigniter实现多文件上传并创建多个缩略图
Jun 12 PHP
PHP采用curl模仿用户登陆新浪微博发微博的方法
Nov 07 PHP
又十个超级有用的PHP代码片段
Sep 24 PHP
PHP实现动态执行代码的方法
Mar 25 PHP
详解PHP的Yii框架中的Controller控制器
Mar 29 PHP
THinkPHP获取客户端IP与IP地址查询的方法
Nov 14 PHP
php类自动装载、链式操作、魔术方法实现代码
Jul 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
自动把纯文本转换成Web页面的php代码
2009/08/27 PHP
php 中文字符串首字母的获取函数分享
2013/11/04 PHP
PHP中Trait及其应用详解
2017/02/14 PHP
php通过header发送自定义数据方法
2018/01/18 PHP
PHP函数积累总结
2019/03/19 PHP
javascript中的undefined 与 null 的区别  补充篇
2010/03/17 Javascript
JQuery与Ajax调用新浪API获取短网址的代码
2014/02/07 Javascript
JS基于clipBoard.js插件实现剪切、复制、粘贴
2016/05/03 Javascript
Node.js 的模块知识汇总
2017/08/16 Javascript
基于JavaScript实现新增内容滚动播放效果附完整代码
2017/08/24 Javascript
JavaScript中利用Array filter() 方法压缩稀疏数组
2018/02/24 Javascript
JS表单传值和URL编码转换
2018/03/03 Javascript
vue slot与传参实例代码讲解
2019/04/28 Javascript
vue.js 打包时出现空白页和路径错误问题及解决方法
2019/06/26 Javascript
解决layui富文本编辑器图片上传无法回显的问题
2019/09/18 Javascript
微信内置浏览器图片查看器的代码实例
2019/10/08 Javascript
《javascript设计模式》学习笔记五:Javascript面向对象程序设计工厂模式实例分析
2020/04/08 Javascript
vue打开子组件弹窗都刷新功能的实现
2020/09/21 Javascript
一波神奇的Python语句、函数与方法的使用技巧总结
2015/12/08 Python
实用自动化运维Python脚本分享
2018/06/04 Python
对python读取zip压缩文件里面的csv数据实例详解
2019/02/08 Python
numpy 返回函数的上三角矩阵实例
2019/11/25 Python
pycharm 实现光标快速移动到括号外或行尾的操作
2021/02/05 Python
手摸手教你用canvas实现给图片添加平铺水印的实现
2019/08/20 HTML / CSS
美国知名户外用品畅销中心:Sierra Trading Post
2016/07/19 全球购物
俄罗斯和世界各地的酒店预订:Hotels.com俄罗斯
2016/08/19 全球购物
XML文档面试题
2015/08/05 面试题
Java面试题汇总
2015/12/06 面试题
蓬莱阁导游词
2015/02/04 职场文书
关于迟到的检讨书
2015/05/06 职场文书
刮痧观后感
2015/06/05 职场文书
2015暑期社会实践调查报告
2015/07/14 职场文书
盘点2020年适合农村地区创业的项目
2019/10/16 职场文书
python字符串的多行输出的实例详解
2021/06/08 Python
十大最强火系宝可梦,喷火龙上榜,第一名有双火属性
2022/03/18 日漫
vue实现可以快进后退的跑马灯组件
2022/04/08 Vue.js