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 裁剪图片成固定大小代码方法
Sep 09 PHP
php5 non-thread-safe和thread-safe这两个版本的区别分析
Mar 13 PHP
PHP用SAX解析XML的实现代码与问题分析
Aug 22 PHP
php数组函数序列之end() - 移动数组内部指针到最后一个元素,并返回该元素的值
Oct 31 PHP
神盾加密解密教程(二)PHP 神盾解密
Jun 08 PHP
微信API接口大全
Apr 15 PHP
PHP如何实现Unicode和Utf-8编码相互转换
Jul 29 PHP
PHP图像裁剪缩略裁切类源码及使用方法
Jan 07 PHP
PHP设计模式之迭代器模式
Jun 17 PHP
如何离线执行php任务
Feb 21 PHP
PHP与以太坊交互详解
Aug 24 PHP
Laravel 实现数据软删除功能
Aug 21 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
jq的get传参数在utf-8中乱码问题的解决php版
2008/07/23 PHP
php xml-rpc远程调用
2008/12/19 PHP
php学习之数据类型之间的转换代码
2011/05/29 PHP
Codeigniter注册登录代码示例
2014/06/12 PHP
PHP中ltrim与rtrim去除左右空格及特殊字符实例
2016/01/07 PHP
PHP数组的定义、初始化和数组元素的显示实现代码
2016/11/05 PHP
详解PHP发送邮件知识点
2018/05/06 PHP
学习ExtJS(一) 之基础前提
2009/10/07 Javascript
Javascript 面向对象(一)(共有方法,私有方法,特权方法)
2012/05/23 Javascript
JQuery中serialize()用法实例分析
2015/02/06 Javascript
JavaScript sort数组排序方法和自我实现排序方法小结
2016/06/06 Javascript
backbone简介_动力节点Java学院整理
2017/07/14 Javascript
node 利用进程通信实现Cluster共享内存
2017/10/27 Javascript
Vue header组件开发详解
2018/01/26 Javascript
React Native中NavigatorIOS组件的简单使用详解
2018/01/27 Javascript
利用Blob进行文件上传的完整步骤
2018/08/02 Javascript
vue中Element-ui 输入银行账号每四位加一个空格的实现代码
2018/09/14 Javascript
Vue2.x通用编辑组件的封装及应用详解
2019/05/28 Javascript
Vue+Bootstrap收藏(点赞)功能逻辑与具体实现
2020/10/22 Javascript
vue $router和$route的区别详解
2020/12/02 Vue.js
用python实现的去除win下文本文件头部BOM的代码
2013/02/10 Python
python实现批量下载新浪博客的方法
2015/06/15 Python
python定向爬虫校园论坛帖子信息
2018/07/23 Python
python热力图实现简单方法
2021/01/29 Python
纯CSS3打造动感漂亮时尚的扇形菜单
2014/03/18 HTML / CSS
CSS3动画效果回调处理详解
2014/12/10 HTML / CSS
HTML5 虚拟键盘出现挡住输入框的解决办法
2017/02/14 HTML / CSS
阿里巴巴的Oracle DBA笔试题答案-SQL tuning类
2016/04/03 面试题
如果Session Bean得Remove方法一直都不被调用会怎么样
2012/07/14 面试题
出国留学经济担保书
2014/04/01 职场文书
教师产假请假条
2014/04/10 职场文书
关于保护环境的标语
2014/06/09 职场文书
网络工程专业大学生求职信
2014/10/01 职场文书
会计人员岗位职责
2015/02/03 职场文书
观后感开头
2015/06/19 职场文书
25句企业管理语录:助你迅速打开思路,句句经典!
2020/01/14 职场文书