在 Python 中使用协程实现 "SystemCalls"
Implementing "SystemCalls" with Coroutines in Python
我目前正在阅读教程文档 http://www.dabeaz.com/coroutines/Coroutines.pdf 并卡在了(纯协程)多任务部分,特别是系统调用部分。
让我困惑的部分是
class Task(object):
taskid = 0
def __init__(self,target):
Task.taskid += 1
self.tid = Task.taskid # Task ID
self.target = target # Target coroutine
self.sendval = None # Value to send
def run(self):
return self.target.send(self.sendval)
def foo():
mytid = yield GetTid()
for i in xrange(5):
print "I'm foo", mytid
yield
class SystemCall(object):
def handle(self):
pass
class Scheduler(object):
def __init__(self):
self.ready = Queue()
self.taskmap = {}
def new(self, target):
newtask = Task(target)
self.taskmap[newtask.tid] = newtask
self.schedule(newtask)
return newtask.tid
def schedule(self, task):
self.ready.put(task)
def mainloop(self):
while self.taskmap:
task = self.ready.get()
try:
result = task.run()
if isinstance(result,SystemCall):
result.task = task
result.sched = self
result.handle()
continue
except StopIteration:
self.exit(task)
continue
self.schedule(task)
实际调用
sched = Scheduler()
sched.new(foo())
sched.mainloop()
我不明白的部分是,tid 是如何在 foo() 中分配给 mytid 的?事物的先后顺序,好像是这样的(从sched.mainloop()开始)。流程有误请指正
假设:让我们说出一些事情
the_coroutine = foo()
scheduler = Scheduler
the_task = scheduler.new(the_coroutine) # assume .new() returns the task instead of tid
- 调度器:.mainloop()被调用
- 调度器:the_task.run()被调用
- the_task: the_coroutine.send(无)被调用
- the_corou: yield GetTid(),并且returns一个GetTid的实例给scheduler在3中的None发送到循环中的yield语句之前。 (我说得对吗?)
- the_corou:(也是同时?)myTid被赋值为GetTid()的实例?
- 调度器:result =
theTask.run() the_task.run()
- 调度器:result确实是SystemCall的一个实例
- 调度器:result.handle()被调用
- GetTid 实例:scheduler.schedule(the_task)
- 调度程序:当前迭代已完成,开始新的迭代
- 调度程序:(假设队列中没有其他任务)the_task.run() 被调用
- 调度器:the_coroutine.send()被调用
- 我迷路了?
当它到达第 12 步时,显然循环已经开始并且能够在调度程序运行相应任务时打印 tid。然而,foo()中的mytid的tid值到底是什么时候赋值的呢?我确定我在流程中遗漏了一些东西,但是在哪里(甚至完全错误)?
然后我注意到任务对象调用 .send() 的部分,它 returns 一个值,所以 .send() returns 一个值?
您似乎省略了 Scheduler 的 new
方法,这几乎肯定是分配发生的地方。我想象 GetTid() 产生的结果会立即使用 .send() 发送回协程。
关于 .send(),是的,你是对的,.send() return是一个值。
尝试下面的例子看看发生了什么。 .send 为 = yield
左侧的变量赋值,协程内的代码继续执行,直到遇到下一个 yield
。在这一点上,协同程序产生,无论它产生什么,都是 .send.
的 return 值
>>> def C():
... x = yield
... yield x**2
... print 'no more yields left'
...
>>> cor = C()
>>> cor.next()
>>> yielded = cor.send(10)
>>> print yielded
100
>>> cor.next()
no more yields left
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
将部分评论点移至答案中
那么,x = yield y
是如何运作的?
当协程遇到该行代码时,它 yields
值 y,然后停止执行,等待有人调用它的带有参数的 .send()
方法。
当有人确实调用 .send()
时,无论 .send
中的参数是什么都被分配给变量 x
,并且协程从那里开始执行代码,直到它的下一个点yield
声明。
编辑: 天哪,它变得更复杂了……我之前浏览过 David Beazley 的演讲,但老实说,我更熟悉他的另外两个演讲在发电机...
通过链接 material,看起来 GetTid 的这个定义就是我们所追求的。
class GetTid(SystemCall):
def handle(self):
self.task.sendval = self.task.tid
self.sched.schedule(self.task)
我引用他的演讲:"The operation of this is little subtle"。哈哈
现在看看主循环:
if isinstance(result,SystemCall):
result.task = task
result.sched = self
result.handle() # <- This bit!
continue
result
这里是 GetTid
对象,运行 它的 handle
方法设置它的 task
的 sendval
属性到任务的 tid
,然后通过将任务放回队列来安排任务。
一旦从队列中检索到任务,task.run()
方法又是 运行。让我们看看任务对象定义:
class Task(object):
...
def run(self):
return self.target.send(self.sendval)
当第二次调用 task.run()
时,它会将它的 sendval
值(之前由 result.handle()
设置为它的 tid
)发送到它的 .target
- foo
协程。这是 foo
协程对象最终接收其 mytid
值的地方。
foo
协同程序对象 运行s 直到它的下一个 yield
,沿途打印它的消息,并且 returns None
(因为有yield
右侧没有任何内容)。 None
是 task.run()
方法的 return 值。
这不是 SystemCall
的一个实例,因此任务在第二次通过时不是 handled/scheduled。
其他邪恶的事情也可能发生,但这就是我现在看到的流程。
我目前正在阅读教程文档 http://www.dabeaz.com/coroutines/Coroutines.pdf 并卡在了(纯协程)多任务部分,特别是系统调用部分。
让我困惑的部分是
class Task(object):
taskid = 0
def __init__(self,target):
Task.taskid += 1
self.tid = Task.taskid # Task ID
self.target = target # Target coroutine
self.sendval = None # Value to send
def run(self):
return self.target.send(self.sendval)
def foo():
mytid = yield GetTid()
for i in xrange(5):
print "I'm foo", mytid
yield
class SystemCall(object):
def handle(self):
pass
class Scheduler(object):
def __init__(self):
self.ready = Queue()
self.taskmap = {}
def new(self, target):
newtask = Task(target)
self.taskmap[newtask.tid] = newtask
self.schedule(newtask)
return newtask.tid
def schedule(self, task):
self.ready.put(task)
def mainloop(self):
while self.taskmap:
task = self.ready.get()
try:
result = task.run()
if isinstance(result,SystemCall):
result.task = task
result.sched = self
result.handle()
continue
except StopIteration:
self.exit(task)
continue
self.schedule(task)
实际调用
sched = Scheduler()
sched.new(foo())
sched.mainloop()
我不明白的部分是,tid 是如何在 foo() 中分配给 mytid 的?事物的先后顺序,好像是这样的(从sched.mainloop()开始)。流程有误请指正
假设:让我们说出一些事情
the_coroutine = foo()
scheduler = Scheduler
the_task = scheduler.new(the_coroutine) # assume .new() returns the task instead of tid
- 调度器:.mainloop()被调用
- 调度器:the_task.run()被调用
- the_task: the_coroutine.send(无)被调用
- the_corou: yield GetTid(),并且returns一个GetTid的实例给scheduler在3中的None发送到循环中的yield语句之前。 (我说得对吗?)
- the_corou:(也是同时?)myTid被赋值为GetTid()的实例?
- 调度器:result =
theTask.run()the_task.run() - 调度器:result确实是SystemCall的一个实例
- 调度器:result.handle()被调用
- GetTid 实例:scheduler.schedule(the_task)
- 调度程序:当前迭代已完成,开始新的迭代
- 调度程序:(假设队列中没有其他任务)the_task.run() 被调用
- 调度器:the_coroutine.send()被调用
- 我迷路了?
当它到达第 12 步时,显然循环已经开始并且能够在调度程序运行相应任务时打印 tid。然而,foo()中的mytid的tid值到底是什么时候赋值的呢?我确定我在流程中遗漏了一些东西,但是在哪里(甚至完全错误)?
然后我注意到任务对象调用 .send() 的部分,它 returns 一个值,所以 .send() returns 一个值?
您似乎省略了 Scheduler 的 new
方法,这几乎肯定是分配发生的地方。我想象 GetTid() 产生的结果会立即使用 .send() 发送回协程。
关于 .send(),是的,你是对的,.send() return是一个值。
尝试下面的例子看看发生了什么。 .send 为 = yield
左侧的变量赋值,协程内的代码继续执行,直到遇到下一个 yield
。在这一点上,协同程序产生,无论它产生什么,都是 .send.
>>> def C():
... x = yield
... yield x**2
... print 'no more yields left'
...
>>> cor = C()
>>> cor.next()
>>> yielded = cor.send(10)
>>> print yielded
100
>>> cor.next()
no more yields left
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
将部分评论点移至答案中
那么,x = yield y
是如何运作的?
当协程遇到该行代码时,它 yields
值 y,然后停止执行,等待有人调用它的带有参数的 .send()
方法。
当有人确实调用 .send()
时,无论 .send
中的参数是什么都被分配给变量 x
,并且协程从那里开始执行代码,直到它的下一个点yield
声明。
编辑: 天哪,它变得更复杂了……我之前浏览过 David Beazley 的演讲,但老实说,我更熟悉他的另外两个演讲在发电机... 通过链接 material,看起来 GetTid 的这个定义就是我们所追求的。
class GetTid(SystemCall):
def handle(self):
self.task.sendval = self.task.tid
self.sched.schedule(self.task)
我引用他的演讲:"The operation of this is little subtle"。哈哈
现在看看主循环:
if isinstance(result,SystemCall):
result.task = task
result.sched = self
result.handle() # <- This bit!
continue
result
这里是 GetTid
对象,运行 它的 handle
方法设置它的 task
的 sendval
属性到任务的 tid
,然后通过将任务放回队列来安排任务。
一旦从队列中检索到任务,task.run()
方法又是 运行。让我们看看任务对象定义:
class Task(object):
...
def run(self):
return self.target.send(self.sendval)
当第二次调用 task.run()
时,它会将它的 sendval
值(之前由 result.handle()
设置为它的 tid
)发送到它的 .target
- foo
协程。这是 foo
协程对象最终接收其 mytid
值的地方。
foo
协同程序对象 运行s 直到它的下一个 yield
,沿途打印它的消息,并且 returns None
(因为有yield
右侧没有任何内容)。 None
是 task.run()
方法的 return 值。
这不是 SystemCall
的一个实例,因此任务在第二次通过时不是 handled/scheduled。
其他邪恶的事情也可能发生,但这就是我现在看到的流程。