我有一些解析应用程序,它基本上执行以下操作
start()
方法添加到IOLoop作为回调以在下一次迭代时调用start()
调用另一个函数,我们称之为get_input()
get_input()
是一个协同程序,它通过网络获取一些数据,然后通过在第一步中添加process_input()
添加它来安排另一个协程start()
。get_input()
还检查某些条件,这取决于获取的数据,并可以使用调整后的参数进行自我调度现在,在这个条件呈现False
后,我知道不会有任何新的输入项需要处理。
但我怎么知道还没有get_input()
和process_input()
的期货尚未解决?
我想这可以通过实现一种计数器来解决,计数器在每次调用process_input()
时递增,在解决后递减。
但是,如果有一系列不同的协程?我如何跟踪他们的状态,这样如果我停止IOLoop,在解决之前没有任务会死掉?
也许,应该有某种分层计数...
编辑:
2 @dano 好的,我现在看到......我很不专心。你真的不会阻止,因为它自己的呼叫在这个列表中 但是!
yield
构造,否则add_callback
否则我们将失去“等待”概念我今天想出的是“未来”
我创建了一个裸Future()
对象。
我用我的装饰器装饰每个@coroutine
启用的方法,它在“metafuture”中增加计数器字段,并为它们的未来添加一个自定义完成的回调,这应该减少它。
当它达到零时,“metafuture”通过调用set_result(None)
来解决
这也是一个IOLoop回调,恰好产生了这个元素:
@coroutine
def wait_for_complete(self):
yield self._input_data_collected
yield self._all_futures_resolved
self.complete()
所以在那之后我们知道没有未来的未来。这是一种比手动实现引用计数更难的方法,但它也涵盖IOLoop.add_callback()
添加任务的方式
答案 0 :(得分:4)
您可以编写方法,以便在完成所有工作之前不返回,而不是安排回调。然后你可以调用IOLoop.run_sync(start)
,直到所有处理完成后才会返回调用:
from tornado import gen
from tornado.ioloop import IOLoop
@gen.coroutine
def start():
yield get_input()
@gen.coroutine
def get_input(*args, **kwargs):
data = yield fetch_data_over_net()
futs = [] # list of Future objects
futs.append(process_input(data))
if should_call_myself(data):
futs.append(get_input(newargs, newkwargs))
yield futs # This will wait for all Future objects in the list to complete.
@gen.coroutine
def process_input(data):
# do stuff
if __name__ == "__main__":
IOLoop.instance().run_sync(start)
我们利用协程返回Futures
的事实,并且Tornado支持waiting for multiple Futures in parallel,以便我们可以同时运行,而不会实际从get_input
返回(和因此start
)在完成所有相关工作之前。