使用 asyncio 的多个循环

Posted

技术标签:

【中文标题】使用 asyncio 的多个循环【英文标题】:Multiple loops with asyncio 【发布时间】:2014-12-10 13:54:54 【问题描述】:

是否可以使用 asyncio 进行多个循环?如果回答是肯定的,我该怎么做? 我的用例是: * 我从一个异步网站列表中提取 url * 对于每个“子 url 列表”,我会在异步中抓取它们/

url提取示例:

import asyncio
import aiohttp
from suburls import extractsuburls

@asyncio.coroutine
def extracturls(url):
    subtasks = []
    response = yield from aiohttp.request('GET', url)
    suburl_list = yield from response.text()
    for suburl in suburl_list:
        subtasks.append(asyncio.Task(extractsuburls(suburl)))
     loop = asyncio.get_event_loop()
     loop.run_until_complete(asyncio.gather(*subtasks))

 if __name__ == '__main__':
     urls_list = ['http://example1.com', 'http://example2.com']
     for url in url_list: 
          subtasks.append(asyncio.Task(extractsuburls(url)))  
     loop = asyncio.get_event_loop()
     loop.run_until_complete(asyncio.gather(*subtasks))
     loop.close()

如果我执行此代码,当 python 尝试启动第二个循环时,我会遇到错误,女巫说一个循环已经在运行。

P.S:我的模块“extractsuburls”使用 aiohttp 来执行 web 请求。

编辑:

好吧,我已经尝试过这个解决方案:

import asyncio
import aiohttp
from suburls import extractsuburls

@asyncio.coroutine
def extracturls( url ):
    subtasks = []
    response = yield from aiohttp.request('GET', url)
    suburl_list = yield from response.text()
    jobs_loop = asyncio.new_event_loop()
    for suburl in suburl_list:
        subtasks.append(asyncio.Task(extractsuburls(suburl)))
     asyncio.new_event_loop(jobs_loop)
     jobs_loop.run_until_complete(asyncio.gather(*subtasks))
     jobs_loop.close()

 if __name__ == '__main__':
     urls_list = ['http://example1.com', 'http://example2.com']
     for url in url_list: 
          subtasks.append(asyncio.Task(extractsuburls(url)))  
     loop = asyncio.get_event_loop()
     loop.run_until_complete(asyncio.gather(*subtasks))
     loop.close()

但我有这个错误:循环参数必须与 Future 一致

有什么想法吗?

【问题讨论】:

【参考方案1】:

你不需要几个事件循环,只需在extracturls()协程中使用yield from gather(*subtasks)即可:

import asyncio
import aiohttp
from suburls import extractsuburls

@asyncio.coroutine
def extracturls(url):
    subtasks = []
    response = yield from aiohttp.request('GET', url)
    suburl_list = yield from response.text()
    for suburl in suburl_list:
        subtasks.append(extractsuburls(suburl))
    yield from asyncio.gather(*subtasks)

 if __name__ == '__main__':
     urls_list = ['http://example1.com', 'http://example2.com']
     for url in url_list: 
          subtasks.append(extractsuburls(url))
     loop = asyncio.get_event_loop()
     loop.run_until_complete(asyncio.gather(*subtasks))
     loop.close()

因此,您需要等待子任务,直到 extracturls 完成。

【讨论】:

哇!刚刚好。这正是我想要的。非常感谢安德鲁。 另一个问题安德鲁,使用这段代码,我如何为每个任务设置超时?如果任务花费超过 10 秒,我想杀死或引发异常。有可能吗? 当然!只需将任务协程包装到 wait_for 调用中,如 asyncio.Task(asyncio.wait_for(extractsuburls(url), 10.0)) 不错!非常感谢安德鲁 :) asyncio.gather 接受协程和任务,因此您可以跳过两个显式的asyncio.Task 调用。

以上是关于使用 asyncio 的多个循环的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 asyncio 同时运行无限循环?

Asyncio之EventLoop笔记

使用 asyncio 时,如何让所有正在运行的任务在关闭事件循环之前完成

无法从正在运行的事件循环中调用 asyncio.run()

时间循环 asyncio 协程

asyncio事件循环原理