加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_丽江站长网 (http://www.0888zz.com/)- 科技、建站、数据工具、云上网络、机器学习!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP中使用协同程序实现合作多任务

发布时间:2022-07-23 05:28:20 所属栏目:PHP教程 来源:互联网
导读:PHP5.5一个比较好的新功能是实现对生成器和协同程序的支持。对于生成器,PHP的文档和各种其他的博客文章(就像这一个或这一个)已经有了非常详细的讲解。协同程序相对受到的关注就少了,所以协同程序虽然有很强大的功能但也很难被知晓,解释起来也比较困难
    PHP5.5一个比较好的新功能是实现对生成器和协同程序的支持。对于生成器,PHP的文档和各种其他的博客文章(就像这一个或这一个)已经有了非常详细的讲解。协同程序相对受到的关注就少了,所以协同程序虽然有很强大的功能但也很难被知晓,解释起来也比较困难。
 
  这篇文章指导你通过使用协同程序来实施任务调度,通过实例实现对技术的理解。我将在前三节做一个简单的背景介绍。如果你已经有了比较好的基础,可以直接跳到“协同多任务处理”一节。
 
  生成器
 
  生成器最基本的思想也是一个函数,这个函数的返回值是依次输出,而不是只返回一个单独的值。或者,换句话说,生成器使你更方便的实现了迭代器接口。下面通过实现一个xrange函数来简单说明:
 
  复制代码 代码如下:
 
  <?php
 
  function xrange($start, $end, $step = 1) {
 
      for ($i = $start; $i <= $end; $i += $step) {
 
          yield $i;
 
      }
 
  }
 
  foreach (xrange(1, 1000000) as $num) {
 
      echo $num, "/n";
 
  }
 
  上面这个xrange()函数提供了和PHP的内建函数range()一样的功能。但是不同的是range()函数返回的是一个包含属组值从1到 100万的数组(注:请查看手册)。而xrange()函数返回的是依次输出这些值的一个迭代器,而且并不会真正以数组形式计算。
 
  这种方法的优点是显而易见的。它可以让你在处理大数据集合的时候不用一次性的加载到内存中。甚至你可以处理无限大的数据流。
 
  当然,也可以不同通过生成器来实现这个功能,而是可以通过继承Iterator接口实现。通过使用生成器实现起来会更方便,而不用再去实现iterator接口中的5个方法了。
 
  生成器为可中断的函数
 
  要从生成器认识协同程序,理解它们内部是如何工作的非常重要:生成器是可中断的函数,在它里面,yield构成了中断点。
 
  紧接着上面的例子,如果你调用xrange(1,1000000)的话,xrange()函数里代码没有真正地运行。相反,PHP只是返回了一个实现了迭代器接口的 生成器类实例:
 
  复制代码 代码如下:
 
  <?php
 
  $range = xrange(1, 1000000);
 
  var_dump($range); // object(Generator)#1
 
  var_dump($range instanceof Iterator); // bool(true)
 
  你对某个对象调用迭代器方法一次,其中的代码运行一次。例如,如果你调用$range->rewind(),那么xrange()里的代码运 行到控制流 第一次出现yield的地方。在这种情况下,这就意味着当$i=$start时yield $i才运行。传递给yield语句的值是使用$range->current()获取的。
 
   为了继续执行生成器中的代码,你必须 调用$range->next()方法。这将再次启动生成器,直到yield语句出现。因此,连续调用next()和current()方法 你将能从生成器里获得所有的值,直到某个点没有再出现yield语句。对xrange()来说,这种情形出现在$i超过$end时。在这中情况下, 控制流将到达函数的终点,因此将不执行任何代码。一旦这种情况发生,vaild()方法将返回假,这时迭代结束。
 
  协程
 
  协程给上面功能添加的主要东西是回送数据给生成器的能力。这将把生成器到调用者的单向通信转变为两者之间的双向通信。
 
  通过调用生成器的send()方法而不是其next()方法传递数据给协程。下面的logger()协程是这种通信如何运行的例子:
 
  复制代码 代码如下:
 
  <?php
 
  function logger($fileName) {
 
      $fileHandle = fopen($fileName, 'a');
 
      while (true) {
 
          fwrite($fileHandle, yield . "/n");
 
      }
 
  }
 
  $logger = logger(__DIR__ . '/log');
 
  $logger->send('Foo');
 
  $logger->send('Bar')
 
  正如你能看到,这儿yield没有作为一个语句来使用,而是用作一个表达式。即它有一个返回值。yield的返回值是传递给send()方法的值。 在这个例子里,yield将首先返回"Foo",然后返回"Bar"。
 
  上面的例子里yield仅作为接收者。混合两种用法是可能的,即既可接收也可发送。接收和发送通信如何进行的例子如下:
 
  复制代码 代码如下:
 
  <?php
 
  function gen() {
 
      $ret = (yield 'yield1');
 
      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表达式两边使用 圆括号不是偶然。由于技术原因(虽然我已经考虑为赋值增加一个异常,就像Python那样),圆括号是必须的。第二点,你可能已经注意到 调用current()之前没有调用rewind()。如果是这么做的,那么已经隐含地执行了rewind操作。
 

(编辑:应用网_丽江站长网)

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

    热点阅读