zoukankan      html  css  js  c++  java
  • php的协程

    协程

    • 协程的支持是在迭代生成器的基础上, 增加了可以回送数据给生成器的功能(调用者发送数据给被调用的生成器函数). 这就把生成器到调用者的单向通信转变为两者之间的双向通信.

    • 传递数据的功能是通过迭代器的send()方法实现的. 下面的logger()协程是这种通信如何运行的例子:

    <?php
    function logger($fileName) {
        $fileHandle = fopen($fileName, 'a');
        while (true) {
            fwrite($fileHandle, yield . "
    ");
        }
    }
     
    $logger = logger(__DIR__ . '/log');
    $logger->send('Foo');
    $logger->send('Bar')
    ?>
    
    • 正如你能看到, 这儿yield没有作为一个语句来使用, 而是用作一个表达式, 即它能被演化成一个值. 这个值就是调用者传递给send()方法的值. 在这个例子里, yield表达式将首先被”Foo”替代写入Log, 然后被”Bar”替代写入Log.

    • 上面的例子里演示了yield作为接受者, 接下来我们看如何同时进行接收和发送的例子:

    <?php
    function gen() {
        $ret = (yield 'yield1');  //这儿yield没有作为一个语句来使用, 而是用作一个表达式, 即它能被演化成一个值. 这个值就是调用者传递给send()方法的值
        var_dump($ret);
        $ret = (yield 'yield2');
        var_dump($ret);
    }
     
    $gen = gen();
    var_dump($gen->current());        // string(6) "yield1"
    var_dump($gen->send('ret1'));     // string(4) "ret1"   (the first var_dump in gen)
                                                          // string(6) "yield2" (the var_dump of the ->send() return value)
    var_dump($gen->send('ret2'));     // string(4) "ret2"   (again from within gen)
                                                          // NULL               (the return value of ->send())
    ?>
    
    • 要很快的理解输出的精确顺序可能稍微有点困难, 但你确定要搞清楚为什按照这种方式输出. 以便后续继续阅读.

    • 另外, 我要特别指出的有两点:

    • 第一点,yield表达式两边的括号在PHP7以前不是可选的, 也就是说在PHP5.5和PHP5.6中圆括号是必须的.

    • 第二点,你可能已经注意到调用current()之前没有调用rewind().这是因为生成迭代对象的时候已经隐含地执行了rewind操作.

    多任务协作

    • 如果阅读了上面的logger()例子, 你也许会疑惑“为了双向通信我为什么要使用协程呢?我完全可以使用其他非协程方法实现同样的功能啊?”, 是的, 你是对的, 但上面的例子只是为了演示了基本用法, 这个例子其实并没有真正的展示出使用协程的优点.

    • 正如上面介绍里提到的,协程是非常强大的概念,不过却应用的很稀少而且常常十分复杂.要给出一些简单而真实的例子很难.

    • 在这篇文章里,我决定去做的是使用协程实现多任务协作.我们要解决的问题是你想并发地运行多任务(或者“程序”).不过我们都知道CPU在一个时刻只能运行一个任务(不考虑多核的情况).因此处理器需要在不同的任务之间进行切换,而且总是让每个任务运行 “一小会儿”.

    • 多任务协作这个术语中的“协作”很好的说明了如何进行这种切换的:它要求当前正在运行的任务自动把控制传回给调度器,这样就可以运行其他任务了. 这与“抢占”多任务相反, 抢占多任务是这样的:调度器可以中断运行了一段时间的任务, 不管它喜欢还是不喜欢. 协作多任务在Windows的早期版本(windows95)和Mac OS中有使用, 不过它们后来都切换到使用抢先多任务了. 理由相当明确:如果你依靠程序自动交出控制的话, 那么一些恶意的程序将很容易占用整个CPU, 不与其他任务共享.

    • 现在你应当明白协程和任务调度之间的关系:yield指令提供了任务中断自身的一种方法, 然后把控制交回给任务调度器. 因此协程可以运行多个其他任务. 更进一步来说, yield还可以用来在任务和调度器之间进行通信.

    • 为了实现我们的多任务调度, 首先实现“任务” — 一个用轻量级的包装的协程函数:

    <?php
    class Task {
        protected $taskId;
        protected $coroutine;
        protected $sendValue = null;
        protected $beforeFirstYield = true;
     
        public function __construct($taskId, Generator $coroutine) {
            $this->taskId = $taskId;
            $this->coroutine = $coroutine;
        }
     
        public function getTaskId() {
            return $this->taskId;
        }
     
        public function setSendValue($sendValue) {
            $this->sendValue = $sendValue;
        }
     
        public function run() {
            if ($this->beforeFirstYield) {
                $this->beforeFirstYield = false;
                return $this->coroutine->current();
            } else {
                $retval = $this->coroutine->send($this->sendValue);
                $this->sendValue = null;
                return $retval;
            }
        }
     
        public function isFinished() {
            return !$this->coroutine->valid();
        }
    }
    

    如代码, 一个任务就是用任务ID标记的一个协程(函数). 使用setSendValue()方法, 你可以指定哪些值将被发送到下次的恢复(在之后你会了解到我们需要这个), run()函数确实没有做什么, 除了调用send()方法的协同程序, 要理解为什么添加了一个 beforeFirstYieldflag变量, 需要考虑下面的代码片段:

    <?php
    function gen() {
        yield 'foo';
        yield 'bar';
    }
     
    $gen = gen();
    var_dump($gen->send('something'));
     
    // 如之前提到的在send之前, 当$gen迭代器被创建的时候一个renwind()方法已经被隐式调用
    // 所以实际上发生的应该类似:
    //$gen->rewind();
    //var_dump($gen->send('something'));
     
    //这样renwind的执行将会导致第一个yield被执行, 并且忽略了他的返回值.
    //真正当我们调用yield的时候, 我们得到的是第二个yield的值! 导致第一个yield的值被忽略.
    //string(3) "bar"
    
    • 通过添加 beforeFirstYieldcondition 我们可以确定第一个yield的值能被正确返回.

    • 调度器现在不得不比多任务循环要做稍微多点了, 然后才运行多任务:

    时间原因,暂停一下

  • 相关阅读:
    【xsy2479】counting 生成函数+多项式快速幂
    【 2019北京集训测试赛(十)】 子矩阵 二分
    【2019北京集训测试赛(七)】 操作 分治+FFT+生成函数
    Python 导出项目依赖/导出所有安装模块/打包数据
    数据结构与算法 介绍以及常见的算法排序
    爬虫 scrapy框架
    爬虫 开发者工具详解
    爬虫 selenium+Xpath 爬取动态js页面元素内容
    爬虫 解析库re,Beautifulsoup,
    爬虫 requests模块的其他用法 抽屉网线程池回调爬取+保存实例,gihub登陆实例
  • 原文地址:https://www.cnblogs.com/lz0925/p/11431389.html
Copyright © 2011-2022 走看看