[[huangyi]]/2006-09-05. 今天邮件列表里讨论 python2.5 中增强的yield特性,讨论得热火朝天。 顿觉心血来潮,就用这增强型的 yield 大致模拟了下 StacklessPython 的 api。 虽然不能一模一样,不过感觉也只能做到这一步了。 主要是因为相对 StacklessPython 来说 yield 有这么几个限制: * 函数只能通过 yield 来挂起,这导致实现 channel 的时候只能通过 {{{yield command(c.send, value)}}},{{{value = yield command(c.receive)}}} 这样的语法来使当前 tasklet 挂起 (一定条件下) ,不像 stacklesspython 直接 some_channel.send(...) 就有可能挂起当前 tasklet 。 * yield 只能向上一层,而不能直接 yield 到调用栈的最上层(或者甚至是指定 yield 到哪一层!!),这导致我们的 tasklet 只能在函数调用的第一层进行 yield 才能将执行权切换给调度程序,像下面: {{{#!python def a_task(): a_func() def a_func(): # 在这个函数里是没用办法将执行权切换给调度程序的。 }}} 不像 StacklessPython ,随便哪里调用 stackless.schedule() 都可以把执行权交出去。 谁叫人家c写的呢。:( 本来还想把 [[evolution]] 也转过来的,发现 pygame 还没为 windows 编译 python2.5 的版本,自己编译太麻烦,还是等 python2.5 正式发布了再说吧。 * 注意:运行下面程序需要 python2.5 {{{#!python #coding:utf-8 from collections import deque ''' http://www.stackless.com/ ''' debuglevel = 0 readys = deque() # 就绪队列 class command(object): def __init__(self, func, *args, **kw): self.func = func self.args = args self.kw = kw def __call__(self, task): return self.func(task, *self.args, **self.kw) class channel(object): ''' http://www.stackless.com/wiki/Channels ''' def __init__(self): self.senders = deque() # 发送队列 元素:(tasklet, obj) self.receivers = deque() # 接收队列 元素:tasklet def send(self, sender, obj): '''向channel中发送数据,如果没用接受者,则让该tasklet等待''' if debuglevel: print 'tasklet:',sender,'send data:',obj,';receivers:',len(self.receivers) if self.receivers: receiver = self.receivers.popleft() ready(sender, None) run(receiver, obj) else: self.senders.append( (sender, obj) ) def receive(self, receiver): ''' 从channel中接收数据,如果没用发送者,则让该tasklet等待''' if debuglevel: print 'tasklet:',receiver,'receive data ;senders:',len(self.senders) if self.senders: sender, obj = self.senders.popleft() ready(receiver,obj) run(sender, None) else: self.receivers.append(receiver) class tasklet(object): '''http://www.stackless.com/wiki/Tasklets''' def __init__(self, func): self.func = func def __call__(self, *arg, **kw): '''将 genarator 加到就绪队列''' ready(self.func(*arg, **kw), None) def run(task, obj): ''' 执行task ''' try: result = task.send(obj) except StopIteration: pass else: if isinstance(result, command): # 给tasklet以执行 "系统命令" 的机会 # 用户代码: yield command(func, ... ) result(task) else: ready(task, None) # 加到就绪队列队尾,等待调度执行 def ready(task, obj): ''' 加入就绪队列 等待调度 ''' readys.append( (task, obj) ) def schedule(): ''' 调度就绪队列中的 tasklet ''' while readys: task, obj = readys.popleft() run(task, obj) # ============ test =========== def simple_task(a): while True: print a yield None def receiver(id, c): value = yield command(c.receive) print id,'receive', value while value: value = yield command(c.receive) print id,'received', value def sender(id, c): value = 20 while value: print id,'send',value yield command(c.send, value) value -= 1 def test_simple(): tasklet(simple_task)(0) tasklet(simple_task)(10) tasklet(simple_task)(100) def test_channel(): c = channel() tasklet(receiver)('receiver1', c) tasklet(receiver)('receiver2', c) tasklet(sender)('sender1', c) c1 = channel() tasklet(receiver)('receiver3', c1) tasklet(sender)('sender2', c1) if __name__=='__main__': #test_simple() test_channel() schedule() }}}