如何使用异步在集合/列表理解中使用并行化?

时间:2018-09-18 05:22:27

标签: python parallel-processing list-comprehension python-asyncio set-comprehension

我想在Python 3.7中创建一个多进程理解。

这是我的代码:

async def _url_exists(url):
  """Check whether a url is reachable"""
  request = requests.get(url)
  return request.status_code == 200:

async def _remove_unexisting_urls(rows):
  return {row for row in rows if await _url_exists(row[0])}

rows = [
  'http://example.com/',
  'http://example.org/',
  'http://foo.org/',
]
rows = asyncio.run(_remove_unexisting_urls(rows))

在此代码示例中,我想从列表中删除不存在的URL。 (请注意,我使用的是集合而不是列表,因为我也想删除重复项。)

我的问题是我仍然看到执行是顺序执行的。 HTTP请求使执行等待。 与串行执行相比,执行时间是相同的。

  • 我做错什么了吗?
  • 如何将这些await / async关键字与python理解一起使用?

2 个答案:

答案 0 :(得分:1)

asyncio本身不会并发运行不同的async函数。但是,使用multiprocessing模块的Pool.map,您可以安排函数在另一个进程中运行:

from multiprocessing.pool import Pool

pool = Pool()

def fetch(url):
    request = requests.get(url)
    return request.status_code == 200

rows = [
  'http://example.com/',
  'http://example.org/',
  'http://foo.org/',
]
rows = [r for r in pool.map(fetch, rows) if r]

答案 1 :(得分:1)

requests不支持asyncio。如果要进行真正的异步执行,则必须查看aiohttpasks

之类的库。

应该在卸载任务之前构建您的集合,这样您甚至不必执行重复操作,而是简化结果。

使用requests本身,您可以退回到run_in_executor,它将在ThreadPoolExecutor内执行您的请求,因此不是真正的异步I / O:

import asyncio
import time
from requests import exceptions, get

def _url_exists(url):
    try:
        r = get(url, timeout=10)
    except (exceptions.ConnectionError, exceptions.ConnectTimeout):
        return False
    else:
        return r.status_code is 200

async def _remove_unexisting_urls(l, r):
    # making a set from the list before passing it to the futures
    # so we just have three tasks instead of nine
    futures = [l.run_in_executor(None, _url_exists, url) for url in set(r)]
    return [await f for f in futures]

rows = [ # added some dupes
    'http://example.com/',
    'http://example.com/',
    'http://example.com/',
    'http://example.org/',
    'http://example.org/',
    'http://example.org/',
    'http://foo.org/',
    'http://foo.org/',
    'http://foo.org/',
]

loop = asyncio.get_event_loop()
print(time.time())
result = loop.run_until_complete(_remove_unexisting_urls(loop, rows))
print(time.time())
print(result)

输出

1537266974.403686
1537266986.6789136
[False, False, False]

如您所见,初始化线程池会有一定的损失,在这种情况下约为2.3秒。但是,考虑到这三个任务中的每一个都会运行十秒钟直到超时(我的IDE不允许通过代理),所以整个十二秒钟的执行时间看起来是相当并行的。