加入收藏 | 设为首页 | 会员中心 | 我要投稿 广州站长网 (https://www.020zz.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP7下协程的实现方法详解

发布时间:2021-03-30 12:37:30 所属栏目:PHP教程 来源:网络整理
导读:副标题#e# 前言 相信大家都听说过『协程』这个概念吧。 但是有些同学对这个概念似懂非懂,不知道怎么实现,怎么用,用在哪,甚至有些人认为yield就是协程! 我始终相信,如果你无法准确地表达出一个知识点的话,我可以认为你就是不懂。 如果你之前了解过利

接下来就是Scheduler这个重点核心部分,他扮演着调度员的角色。

  • Class Scheduler
    */
    Class Scheduler
    {
    /**
  • @var SplQueue
    */
    protected $taskQueue;
    /**
  • @var int
    */
    protected $tid = 0;
  • /**

    • Scheduler constructor.
      /
      public function __construct()
      {
      /
      原理就是维护了一个队列,
    • 前面说过,从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制
    • */
      $this->taskQueue = new SplQueue();
      }
      /**
    • 增加一个任务
    • @param Generator $task
    • @return int
      */
      public function addTask(Generator $task)
      {
      $tid = $this->tid;
      $task = new Task($tid,$task);
      $this->taskQueue->enqueue($task);
      $this->tid++;
      return $tid;
      }
      /**
    • 把任务进入队列
    • @param Task $task
      */
      public function schedule(Task $task)
      {
      $this->taskQueue->enqueue($task);
      }
      /**
    • 运行调度器
      */
      public function run()
      {
      while (!$this->taskQueue->isEmpty()) {
      // 任务出队
      $task = $this->taskQueue->dequeue();
      $res = $task->run(); // 运行任务直到 yield

    if (!$task->isFinished()) {
    $this->schedule($task); // 任务如果还没完全执行完毕,入队等下次执行
    }
    }
    }
    }

    这样我们基本就实现了一个协程调度器。

    你可以使用下面的代码来测试:

    newTask(task1()); // 添加不同的闭包函数作为任务 $scheduler->newTask(task2()); $scheduler->run();

    关键说下在哪里能用得到PHP协程。

    这样就提高了程序的执行效率。

    关于『系统调用』的实现,鸟哥已经讲得很明白,我这里不再说明。

    3)协程堆栈

    鸟哥文中还有一个协程堆栈的例子。

    我们上面说过了,如果在函数中使用了yield,就不能当做函数使用。

    所以你在一个协程函数中嵌套另外一个协程函数:

    newTask(task()); $scheduler->run();

    这里的echoTimes是执行不了的!所以就需要协程堆栈。

    不过没关系,我们改一改我们刚刚的代码。

    把Task中的初始化方法改下,因为我们在运行一个Task的时候,我们要分析出他包含了哪些子协程,然后将子协程用一个堆栈保存。(C语言学的好的同学自然能理解这里,不理解的同学我建议去了解下进程的内存模型是怎么处理函数调用)

    taskId = $taskId; // $this->coroutine = $coroutine; // 换成这个,实际Task->run的就是stackedCoroutine这个函数,不是$coroutine保存的闭包函数了 $this->coroutine = stackedCoroutine($coroutine); }

    当Task->run()的时候,一个循环来分析:

    current(); // 获取中断点,也就是yield出来的值 if ($value instanceof Generator) { // 如果是也是一个生成器,这就是子协程了,把当前运行的协程入栈保存 $stack->push($gen); $gen = $value; // 把子协程函数给gen,继续执行,注意接下来就是执行子协程的流程了 continue; } // 我们对子协程返回的结果做了封装,下面讲 $isReturnValue = $value instanceof CoroutineReturnValue; // 子协程返回`$value`需要主协程帮忙处理 if (!$gen->valid() || $isReturnValue) { if ($stack->isEmpty()) { return; } // 如果是gen已经执行完毕,或者遇到子协程需要返回值给主协程去处理 $gen = $stack->pop(); //出栈,得到之前入栈保存的主协程 $gen->send($isReturnValue ? $value->getValue() : NULL); // 调用主协程处理子协程的输出值 continue; } $gen->send(yield $gen->key() => $value); // 继续执行子协程 } }

    然后我们增加echoTime的结束标示:

    public function __construct($value) {
    $this->value = $value;
    }
    // 获取能把子协程的输出值给主协程,作为主协程的send参数
    public function getValue() {
    return $this->value;
    }
    }
    function retval($value) {
    return new CoroutineReturnValue($value);
    }

    然后修改echoTimes:

    Task变为:

    这样就实现了一个协程堆栈,现在你可以举一反三了。

    4)PHP7中yield from关键字

    PHP7中增加了yield from,所以我们不需要自己实现携程堆栈,真实太好了。

    把Task的构造函数改回去:

    taskId = $taskId; $this->coroutine = $coroutine; // $this->coroutine = stackedCoroutine($coroutine); //不需要自己实现了,改回之前的 }

    echoTimes函数:

    task1生成器:

    这样,轻松调用子协程。

    总结

    这下应该明白怎么实现PHP协程了吧?

    好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对编程之家的支持。

    (编辑:广州站长网)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读