如何将无限生成器提供给eventlet(或gevent)?

时间:2014-01-04 15:01:15

标签: python asynchronous generator gevent eventlet

eventlet和gevent的文档都有几个关于如何异步生成IO任务并获得结果的例子。 但到目前为止,所有的示例都应该从异步调用中返回一个值,我总是在调用spawn()之后找到一个阻塞调用。 join()joinall()wait()waitall()。 这假设调用使用IO的函数是立即的,我们可以直接跳到我们等待结果的位置。

但在我的情况下,我希望从一个可能很慢或任意大甚至无限的发电机中获得工作。

我显然不能这样做

pile = eventlet.GreenPile(pool)
for url in mybiggenerator():
    pile.spawn(fetch_title, url)
titles = '\n'.join(pile)

因为mybiggenerator()可能需要很长时间才能用尽。因此,当我仍在产生异步调用时,我必须开始使用结果。

这通常可以通过队列资源完成,但我不确定如何。假设我创建了一个队列来保存作业,从名为P的greenlet推送一堆作业并从另一个greenlet C中弹出它们。 在C语言中,如果我发现队列是空的,我怎么知道P是否已经推送了必须推送的每个作业,或者它是否只是在迭代的中间?

Alternativey,Eventlet允许我遍历pile来获取返回值,但是我可以在没有生成我必须生成的所有作业的情况下开始这样做吗?怎么样?这将是一个更简单的选择。

3 个答案:

答案 0 :(得分:0)

使用Py3k中的新concurrent.futures模块,我会说(假设你想要做的处理实际上比join更复杂):

with concurrent.futures.ThreadPoolExecutor(max_workers=foo) as wp:
    res = [wp.submit(fetchtitle, url) for url in mybiggenerator()]
ans = '\n'.join([a for a in concurrent.futures.as_completed(res)]

这样您就可以在所有fetchtitle来电完成之前开始处理结果。但是,它会要求您在继续之前耗尽mybiggenerator - 除非您想设置一些max_urls参数或类似参数,否则您不清楚如何解决此问题。不过,这仍然是你可以用原始实现做的事情。

答案 1 :(得分:0)

默认情况下,您不需要任何池或堆。它们只是实现特定策略的方便包装器。首先,你应该知道你的代码在所有情况下必须如何运作,即:何时以及为什么你开始另一个greenthread,何时以及为什么要等待某事。

如果您对这些问题有一些答案而对其他问题有疑问,请随便提出。同时,这是一个处理无限“生成器”(实际上是队列)的原型。

queue = eventlet.queue.Queue(10000)
wait = eventlet.semaphore.CappedSemaphore(1000)


def fetch(url):
  # httplib2.Http().request
  # or requests.get
  # or urllib.urlopen
  # or whatever API you like
  return response


def crawl(url):
  with wait:
    response = fetch(url)
    links = parse(response)
    for url in link:
      queue.put(url)


def spawn_crawl_next():
  try:
    url = queue.get(block=False)
  except eventlet.queue.Empty:
    return False
  # use another CappedSemaphore here to limit number of outstanding connections
  eventlet.spawn(crawl, url)
  return True


def crawler():
  while True:
    if spawn_crawl_next():
      continue

    while wait.balance != 0:
      eventlet.sleep(1)

    # if last spawned `crawl` enqueued more links -- process them
    if not spawn_crawl_next():
      break


def main():
  queue.put('http://initial-url')
  crawler()

答案 2 :(得分:0)

回复:“来自Python3的concurrent.futures并不真正适用于“ eventlet或gevent”部分。”

实际上,可以将eventlet组合以将并发部署为ThreadPoolExecutor作为GreenThread执行器。

请参阅:https://github.com/zopefiend/green-concurrent.futures-with-eventlet/commit/aed3b9f17ac27eeaf8c56210e0c8e4aff2ecbdb5