python 自身的协程实现?

本来,想给这个随笔起个更眩的名字:《跟随赖神学协程——之一》,原因嘛,自然是因为赖神的协程三篇之一(协程初接触)。不过,怕赖神k我,所以标题党还是朴素一点吧。

至于标题里的问号,是我有意加上去的。原因是在推上赖神认为“python 语言和标准库是不支持协程的(3.x部分支持)”,并且如果 python 自身支持协程,“stackless py 该有多么惭愧啊, :)”(stackless python 的协程详情请看这里)。但是因为 PEP 0342 的描述,令我很迷惑。python 自身是否可以实现协程,这个值得商榷。我不确定,保守点好,给自己个后路走。加个问号吧……如果这不算协程,大家就当我实现了“伪协程”吧。嘿嘿……

在赖神的博客里有对协程的一个简单的描述,为了说明下面的代码,特别摘抄于此:“协程是用户空间线程,操作系统其存在一无所知,所以需要用户自己去做调度,用来执行协作式多任务非常合适。”

赖神博客中的例子应当是引用了维基百科对于协程的说明。下面的代码正是基于这个伪代码实现的,同样的原因摘抄于此:
生产者:

   loop
       while q is not full
           create some new items
           add the items to q
       yield to consume

消费者

   loop
       while q is not empty
           remove some items from q
           use the items
       yield to produce

这两段伪代码,赖神的博客上已经有说明,不再累述。看看 python 如何实现这个的吧:

#!/usr/bin/evn python
# -*- coding:utf-8 -*-
import time
# 生产者
def produce(l):
	i = 0
	while True:
		i += 1
		l.append(i)
		time.sleep(2)
		# 暂时跳出当前方法
		yield i
		# 消费者 send 了以后,就又回到了这里继续执行
		pass

# 消费者
def consume(l):
	p = produce(l)
	while True:
		try:
			# 获取生产者的执行
			i = p.next()
			# 这里完全是为了匹配伪代码而写,实际上这个 list 在这种情况下,即不会多于一个 item,也不会为空
			while len(l) > 0:
				print l.pop()
		except:
			# 生产者提供的所有的执行都处理完了,通过 send 返回生产者跳出的地方
			p.send(None)
l = []
consume(l)

这段代码是可以正常执行的,由生产者每秒产生一个数字放入 list,由消费者从 list 中取出并打印。
大家对照赖神的说明和维基百科上的内容理解一下吧。

总结一下!我对于 python 的协程的观点是:“python 自身已经提供了实现协程的基础条件,是可以很容易的实现概念中的协程。但是由于栈的问题,只能由循环的方式来实现调用。而 stackless 在 python 原有的基础上,改进了 python 在协程上的实现方式,使其代码的表达更加的自然。”

如有不对,尽请拍砖,期待赖神的协程系列二、三。继续品味协程……

Join the Conversation

2 Comments

  1. 我觉得这个例子并没有很好的展示generator或者是yield的功能。
    你这样个例子完全可以在一个函数里面来实现,没有必要使用generator,generator的一个重要的功能就是:它具有记忆能力,可以从上次执行的地方继续执行。
    我觉得如果你的例子中的消费者或者是生产者如果函数如果在yield之后作的功能需要依赖于yield之前的功能的话,那就能很好的展现generator的功能了

Leave a comment

Your email address will not be published. Required fields are marked *