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和ACCESS写聊天室(二)
Oct 09 PHP
php park、unpark、ord 函数使用方法(二进制流接口应用实例)
Oct 19 PHP
php学习之 数组声明
Jun 09 PHP
PHP和JAVA中的重载(overload)和覆盖(override) 介绍
Mar 01 PHP
PHP array_multisort() 函数的深入解析
Jun 20 PHP
对于PHP 5.4 你必须要知道的
Aug 07 PHP
Smarty模板变量调节器用法分析
May 23 PHP
Yii2组件之多图上传插件FileInput的详细使用教程
Jun 20 PHP
PHP入门教程之字符串处理技巧总结(转换,过滤,解析,查找,截取,替换等)
Sep 11 PHP
thinkphp制作404跳转页的简单实现方法
Sep 22 PHP
Yii 使用intervention/image拓展实现图像处理功能
Jun 22 PHP
Laravel 中使用简单的方法跟踪用户是否在线(推荐)
Oct 30 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 zip文件解压类代码
2009/12/02 PHP
php文件上传表单摘自drupal的代码
2011/02/15 PHP
Centos PHP 扩展Xchche的安装教程
2016/07/09 PHP
Laravel 5.5 实现禁用用户注册示例
2019/10/24 PHP
layui数据表格自定义每页条数limit设置
2019/10/26 PHP
php开发最强大的IDE编辑的phpstorm 2020.2配置Xdebug调试的详细教程
2020/08/17 PHP
一个用javascript写的select支持上下键、首字母筛选以及回车取值的功能
2009/09/09 Javascript
document.createElement()用法
2013/03/13 Javascript
一个网页标题title的闪动提示效果实现思路
2014/03/22 Javascript
5个数组Array方法: indexOf、filter、forEach、map、reduce使用实例
2015/01/29 Javascript
JavaScript中setMonth()方法的使用详解
2015/06/11 Javascript
JavaScript的this关键字的理解
2016/06/18 Javascript
AngularJS教程之环境设置
2016/08/16 Javascript
浅析ES6的八进制与二进制整数字面量
2016/08/30 Javascript
利用jQuery.Validate异步验证用户名是否存在(推荐)
2016/12/09 Javascript
Vue 2中ref属性的使用方法及注意事项
2017/06/12 Javascript
Vue-Router进阶之滚动行为详解
2017/09/13 Javascript
vue-cli3脚手架的配置及使用教程
2018/08/28 Javascript
react ant Design手动设置表单的值操作
2020/10/31 Javascript
python脚本实现统计日志文件中的ip访问次数代码分享
2014/08/06 Python
python数据清洗系列之字符串处理详解
2017/02/12 Python
python3爬虫学习之数据存储txt的案例详解
2019/04/24 Python
python的pygal模块绘制反正切函数图像方法
2019/07/16 Python
pandas 如何分割字符的实现方法
2019/07/29 Python
使用Python matplotlib作图时,设置横纵坐标轴数值以百分比(%)显示
2020/05/16 Python
使用Python Tkinter实现剪刀石头布小游戏功能
2020/10/23 Python
StubHub哥伦比亚:购买和出售您的门票
2016/10/20 全球购物
经济实惠的名牌太阳镜和眼镜:Privé Revaux
2021/02/07 全球购物
Shell脚本如何向终端输出信息
2014/04/25 面试题
感恩父母的演讲稿
2014/05/06 职场文书
贷款担保申请书
2014/05/20 职场文书
司法局群众路线教育实践活动整改措施思想汇报
2014/10/13 职场文书
2014年发展党员工作总结
2014/11/12 职场文书
毕业生个人总结
2015/02/28 职场文书
小学副班长竞选稿
2015/11/21 职场文书
MySQL数据管理操作示例讲解
2022/12/24 MySQL