当列表中的任何Future完成时返回

时间:2013-12-25 00:56:36

标签: python tornado coroutine

我有一个协同应该同时执行一些任务:

@gen.coroutine
def scrape_trackers(self):
    yield [self.scrape_tracker(tracker) for tracker in self.torrent.trackers]

有些跟踪器永远不会响应(或者可能在一小时内响应),因此我的代码永远不会超过此方法,因为Tornado正在等待所有的完成。我如何只等待一个完成并继续在后台尝试其他?

类似的东西:

yield WhenAnyComplete(map(self.scrape_tracker, self.torrent.trackers))

我正在考虑用IOLoop.add_callback()调用每个方法并在完成时做一些事情,但我不完全确定从哪里开始:

for tracker in self.torrent.trackers:
    future = self.scrape_tracker(tracker)
    IOLoop.add_future(future, self.tracker_scraped)

感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

我能想到的最好的方法是返回一个新的Future并且第一次成功的函数调用给它一个结果:

def scrape_trackers(self):
    result = Future()

    for tracker in self.torrent.trackers:
        future = self.scrape_tracker(tracker)
        future.add_done_callback(lambda f: self.tracker_done(f, result))

    return result

def tracker_done(self, future, result_future):
    if future.exception():
        logging.warning('Tracker could not be scraped: %s', future.exception())
        return

    logging.info('Scraped tracker %s', future)

    if self.unconnected_peers:
        result_future.set_result(True)

答案 1 :(得分:0)

yieldpoints库具有WaitAny功能。它早于Tornado使用Futures,所以现在使用起来有点麻烦。最好查看yieldpoints.WaitAnytornado.gen.YieldFuture的来源,以便将两者混合起来。

或者,您可以重新组织代码,以便不再需要WaitAny。而不是在完成结果时剥离结果的一个协程,只需为每个任务启动一个新的协程并在最后加入它们:

@gen.coroutine
def process_tracker(self, tracker): 
    result = yield self.scrape_tracker(tracker)
    # Do whatever you'd do after WaitAny here.  All the process_tracker
    # calls can proceed in parallel as their results are ready.

@gen.coroutine
def process_all_trackers(self, trackers):
    yield [self.process_tracker(t) for t in trackers]