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代码
Dec 06 PHP
深入PHP与浏览器缓存的分析
Jun 03 PHP
php fsockopen伪造post与get方法的详解
Jun 14 PHP
页面乱码问题的根源及其分析
Aug 09 PHP
PHP防范SQL注入的具体方法详解(测试通过)
May 09 PHP
php读取文件内容到数组的方法
Mar 16 PHP
8个PHP数组面试题
Jun 23 PHP
PHP使用PHPExcel删除Excel单元格指定列的方法
Jul 06 PHP
php源码 fsockopen获取网页内容实例详解
Sep 24 PHP
php 猴子摘桃的算法
Jun 20 PHP
使用PHP+MySql+Ajax+jQuery实现省市区三级联动功能示例
Sep 15 PHP
详解PHP Swoole与TCP三次握手
May 27 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设计模式之观察者模式的应用详解
2013/05/21 PHP
php ckeditor上传图片文件名乱码解决方法
2013/11/15 PHP
php递归方法实现无限分类实例代码
2014/02/28 PHP
php导出word文档与excel电子表格的简单示例代码
2014/03/08 PHP
支持中文的PHP按字符串长度分割成数组代码
2015/05/17 PHP
Laravel框架实现调用百度翻译API功能示例
2019/05/30 PHP
JavaScript 学习点滴记录
2009/04/24 Javascript
javascript 全等号运算符使用说明
2010/05/31 Javascript
jQuery判断密码强度实现思路及代码
2013/04/24 Javascript
jQuery实现隔行背景色变色
2014/11/24 Javascript
jQuery禁用键盘后退屏蔽F5刷新及禁用右键单击
2016/01/22 Javascript
JS中的二叉树遍历详解
2016/03/18 Javascript
bootstrapValidator bootstrap-select验证不可用的解决办法
2017/01/11 Javascript
jQuery实用密码强度检测
2017/03/02 Javascript
Angularjs 动态添加指令并绑定事件的方法
2017/04/13 Javascript
基于vue-resource jsonp跨域问题的解决方法
2018/02/03 Javascript
layui表格 列自动适应大小失效的解决方法
2019/09/06 Javascript
layui扩展上传组件模拟进度条的方法
2019/09/23 Javascript
零基础写python爬虫之爬虫的定义及URL构成
2014/11/04 Python
Python的函数的一些高阶特性
2015/04/27 Python
python 迭代器和iter()函数详解及实例
2017/03/21 Python
python将文本中的空格替换为换行的方法
2018/03/19 Python
Django框架模板介绍
2019/01/15 Python
详解在python操作数据库中游标的使用方法
2019/11/12 Python
Python使用Matlab命令过程解析
2020/06/04 Python
二手书店创业计划书
2014/01/16 职场文书
社区居务公开实施方案
2014/03/27 职场文书
小学生国旗下演讲稿
2014/04/25 职场文书
学生保证书范文
2014/04/28 职场文书
装饰公司活动策划方案
2014/08/23 职场文书
2014年助理工程师工作总结
2014/11/14 职场文书
护士年终个人总结
2015/02/13 职场文书
2015年教师节活动总结
2015/03/20 职场文书
Python Flask请求扩展与中间件相关知识总结
2021/06/11 Python
Python爬取用户观影数据并分析用户与电影之间的隐藏信息!
2021/06/29 Python
Python开发五子棋小游戏
2022/05/02 Python