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 才能将执行权切换给调度程序,像下面:
不像 StacklessPython ,随便哪里调用 stackless.schedule() 都可以把执行权交出去。
谁叫人家c写的呢。:(
本来还想把 evolution 也转过来的,发现 pygame 还没为 windows 编译 python2.5 的版本,自己编译太麻烦,还是等 python2.5 正式发布了再说吧。
- 注意:运行下面程序需要 python2.5
1 #coding:utf-8
2 from collections import deque
3
4 '''
5 http://www.stackless.com/
6 '''
7
8 debuglevel = 0
9
10 readys = deque() # 就绪队列
11
12 class command(object):
13 def __init__(self, func, *args, **kw):
14 self.func = func
15 self.args = args
16 self.kw = kw
17
18 def __call__(self, task):
19 return self.func(task, *self.args, **self.kw)
20
21 class channel(object):
22 ''' http://www.stackless.com/wiki/Channels '''
23 def __init__(self):
24 self.senders = deque() # 发送队列 元素:(tasklet, obj)
25 self.receivers = deque() # 接收队列 元素:tasklet
26
27 def send(self, sender, obj):
28 '''向channel中发送数据,如果没用接受者,则让该tasklet等待'''
29 if debuglevel:
30 print 'tasklet:',sender,'send data:',obj,';receivers:',len(self.receivers)
31 if self.receivers:
32 receiver = self.receivers.popleft()
33 ready(sender, None)
34 run(receiver, obj)
35 else:
36 self.senders.append( (sender, obj) )
37
38 def receive(self, receiver):
39 ''' 从channel中接收数据,如果没用发送者,则让该tasklet等待'''
40 if debuglevel:
41 print 'tasklet:',receiver,'receive data ;senders:',len(self.senders)
42 if self.senders:
43 sender, obj = self.senders.popleft()
44 ready(receiver,obj)
45 run(sender, None)
46 else:
47 self.receivers.append(receiver)
48
49 class tasklet(object):
50 '''http://www.stackless.com/wiki/Tasklets'''
51 def __init__(self, func):
52 self.func = func
53
54 def __call__(self, *arg, **kw):
55 '''将 genarator 加到就绪队列'''
56 ready(self.func(*arg, **kw), None)
57
58 def run(task, obj):
59 ''' 执行task '''
60 try:
61 result = task.send(obj)
62 except StopIteration:
63 pass
64 else:
65 if isinstance(result, command):
66 # 给tasklet以执行 "系统命令" 的机会
67 # 用户代码: yield command(func, ... )
68 result(task)
69 else:
70 ready(task, None) # 加到就绪队列队尾,等待调度执行
71
72 def ready(task, obj):
73 ''' 加入就绪队列 等待调度 '''
74 readys.append( (task, obj) )
75
76 def schedule():
77 ''' 调度就绪队列中的 tasklet '''
78 while readys:
79 task, obj = readys.popleft()
80 run(task, obj)
81
82 # ============ test ===========
83
84 def simple_task(a):
85 while True:
86 print a
87 yield None
88
89 def receiver(id, c):
90 value = yield command(c.receive)
91 print id,'receive', value
92 while value:
93 value = yield command(c.receive)
94 print id,'received', value
95
96 def sender(id, c):
97 value = 20
98 while value:
99 print id,'send',value
100 yield command(c.send, value)
101 value -= 1
102
103 def test_simple():
104 tasklet(simple_task)(0)
105 tasklet(simple_task)(10)
106 tasklet(simple_task)(100)
107
108 def test_channel():
109 c = channel()
110 tasklet(receiver)('receiver1', c)
111 tasklet(receiver)('receiver2', c)
112 tasklet(sender)('sender1', c)
113 c1 = channel()
114 tasklet(receiver)('receiver3', c1)
115 tasklet(sender)('sender2', c1)
116
117 if __name__=='__main__':
118 #test_simple()
119 test_channel()
120 schedule()