狂拽酷炫吊炸天:用 PHP 协程实现多任务协作

https://www.v2ex.com/t/289499


PHP 5.5 中最重要的特性之一就是对协程( coroutine )和生成器( generator )的支持。生成器的特性已经由官方文档和许多博文(比如这一篇这一篇)讲解得很充分了。另一方面,协程受到的关注则较少。这是因为协程的功能相较而言更加强大,但却难以讲解。

本文会使用协程实现一个任务调度器,以此帮助你理解协程的概念和用法。我会先用几个段落做一些介绍。如果你觉得你已经对生成器和协程的基本概念掌握得很牢固了,那么你可以直接跳至“多任务协作”这一段开始阅读。

生成器

生成器背后最原始的想法就是一个函数不仅仅返回一次数据,而是能够返回一系列的数据,并且这些数据是挨个返回的。也可以理解为,生成器使你能更方便地实现迭代器。xrange() 函数就是一个生成器的简单例子:

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() 函数与内置函数 range() 函数的功能相同。唯一的区别在于 range() 会返回一个包含了一百万个数字的数组,而 xrange() 则返回一个可以吐出这些数字的迭代器,不会去老实地计算出一个包含所有数字的数组。

这样做的好处是显而易见的。它使得你可以处理超大规模的数据,而无需一次性将它们载入内存。你甚至可以处理无穷无尽的数据流。

当然并不是只有生成器能做到这一点,你也可以通过实现一个 iterator 接口来完成同样的工作。生成器只是更加方便,避免你为了生成一个迭代器而不得不去实现该接口的五个不同方法。

将生成器用作可中断函数

要从对生成器的理解过度到协程的概念,理解它们内部的工作方式是非常重要的:生成器是可中断的函数,而 yield 语句则构成了这些中断点。

接着刚才的例子,当你调用 xrange(1, 1000000) 时,实际上 xrange() 没有执行任何代码。取而代之地, PHP 仅返回了一个 Generator 类的实例,它实现了 Iterator 接口:

$range = xrange(1, 1000000);
var_dump($range); // object(Generator)#1
var_dump($range instanceof Iterator); // bool(true)

只有当你调用 iterator 接口相关的方法时代码才会执行。例如,你执行 $range->rewind() 时,xrange() 函数中的代码就会执行,直到流程中的第一条 yield 语句。如此一来,就意味着 $i = $start 和 yield $i 被执行了。任何传递给yield 语句的数据都能通过 $range->current() 来获取。

你需要调用 $range->next() 方法来继续执行生成器中的代码。这样它就会继续执行下去,直到下一条 yield 语句。所以只要连续地调用 ->next() 和 ->current() 方法,你就可以从生成器中获取到所有的返回值,直至最终不再遇到yield 语句。对于 xrange() 函数来说,就是 $i 超出 $end 的时候。如此一来,流程会继续执行完剩余的代码,直至函数的结尾。若此时调用 ->valid() 方法则会返回 false ,这个迭代过程就结束了。

协程

相对于上述功能,协程最主要的一点就是加入了向生成器中发送数据的能力。这使得从生成器到调用者的单向数据流,变成了两者彼此往来的数据通路。

将数据传递给协程的方法是调用 ->send() 方法,而不是 ->next()。下面的这个 logger() 的例子展示了它是如何工作的:

function logger($fileName) {
  $fileHandle = fopen($fileName, 'a');
  while (true) {
    fwrite($fileHandle, yield . "\n");
  }
}

$logger = logger(__DIR__ . '/log');
$logger->send('Foo');
$logger->send('Bar');

如你所见,在这里 yield 没有被用作一个语句,而是作为一个表达式,也是就说它有一个返回值。这个返回值是通过 ->send() 语句传过来的。此例中 yield 会先返回 'Foo' 再返回 'Bar'。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值