2025年3月30日 星期日 甲辰(龙)年 月廿九 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Other

我教我自己:协程(2)

时间:02-25来源:作者:点击数:10

上一篇文章我们说了协程和线程的区别,这次我们来讲讲协程和生成器(Generator).

生成器(Generator)名字里之所以有生成(generate)二字,是因为它的作用:生成器是用来生成值(Value)的。它是一个会返回值的函数。

例如,我们想要一个函数,每次调用它,就会返回给我们一个Fibonacci数;第N次调用它,就返回给我们第N个Fibonacci数:

  • let cur = 0
  • let next = 1
  • const fibo = () => {
  • const p = cur + next
  • cur = next
  • next = p
  • return cur
  • }

用的时候我们有:

  • const p1 = fibo()
  • const p2 = fibo()

从使用角度看,这个函数每调用一次,就生成一个值,所以叫生成器。

在生成器里面,可以有一个yield关键字,用来代替我们上面的return

  • const fibo = () => {
  • while (true) {
  • yield(cur)
  • let p = cur + next
  • cur = next
  • next = p
  • }
  • }

这个生成器每当执行到yield的时候,就会像return一样,先把cur的值返回;并且暂停执行;直到下一次被调用的时候,才会从yield的下一行继续执行。 在会挂起和继续执行的概念上,和协程是很相似的。

实际上,生成器和协程如此相似,以至于有人把它叫做semicoroutine,也就是半协程

生成器和协程的区别主要是以下方面:

  • 使用目的不同。生成器主要是为了获取一系列的值。Wiki上说了一句很经典的话:

A generator looks like a function but behaves like an iterator

生成器看起来像一个函数,用起来像迭代器(或数组)

而协程主要是为了主动让出执行,进行协作式并发。

  • 协程能够明确指定要把执行权力让出给谁(例如启动一个下载以后,让出给UI),而生成器只能返回到调用它的那个函数。

我们可以使用生成器来实现协程。

具体思路是:在生成器返回的时候,启动一个调度器(dispatcher)来明确指定下一个该运行的是谁。下面的例子来自Wiki:

  • var q := new queue
  • generator produce
  • loop
  • while q is not full
  • create some new items
  • add the items to q
  • yield consume
  • generator consume
  • loop
  • while q is not empty
  • remove some items from q
  • use the items
  • yield produce
  • subroutine dispatcher
  • var d := new dictionary(generator → iterator)
  • d[produce] := start produce
  • d[consume] := start consume
  • var current := produce
  • loop
  • current := next d[current]

可以看到,上面的生成器和调度器的配合,是通过函数名字进行的:

  • 首先,每个生成器返回的值,是下一个要运行的生成器名字
  • 其次,调度器维护一个Map,确保能从生成器名字拿到对应的函数;
  • 第三,在一个循环里运行生成器,拿到返回值,再运行下一个对应的生成器。

一些没有实现协程的语言会使用这种方法实现一个协程的库,比如Python2.5以前的版本。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐