了解 python 中的异步
Understanding asyncio in python
我一直很难理解 Python 的 asyncio 模块以及如何不阻塞异步调用。例如,给定此代码片段:
import aiohttp
import asyncio
import async_timeout
async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
async def main(loop):
print(1)
async with aiohttp.ClientSession(loop=loop) as session:
html = await fetch(session, 'http://python.org')
print(html)
print(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
我预计,与 Javascript 中的工作方式类似,输出为
1
2
<!doctype html>
<...>
相反,该函数打印 1,阻塞直到它返回 html,然后打印 2。它为什么阻塞,我如何/如何避免阻塞?感谢您的帮助。
你的问题是这一行:
html = await fetch(session, 'http://python.org')
这意味着 - 等待直到从 fetch 获得响应,或者换句话说,它会阻塞 current 协程,直到 fetch 协程 return 的响应文本。
为了让事情更简单:
- 提取操作本身不阻塞,只有等待。
- await 只阻塞当前协程,而不是线程。
- 就像vincent说的那样,调用操作本身并没有做任何事情(我们不启动抓取过程)
Calling a coroutine does not start its code running – the coroutine
object returned by the call doesn’t do anything until you schedule its
execution. There are two basic ways to start it running: call await
coroutine or yield from coroutine from another coroutine (assuming the
other coroutine is already running!), or schedule its execution using
the ensure_future() function or the AbstractEventLoop.create_task()
method.
和修复(以获得所需的输出):
import asyncio
import aiohttp
import async_timeout
async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
async def main(loop):
print(1)
async with aiohttp.ClientSession(loop=loop) as session:
future = asyncio.ensure_future(fetch(session, 'http://python.org')) # we start the fetch process
print(2)
html = await future # we wait for getting the fetch response
print(html)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
为了理解 python 3.6 async def
风格的 asyncio,我建议你 运行 这个简单的例子:
import asyncio
import time
def write(msg):
print(msg, flush=True)
async def say1():
await asyncio.sleep(1)
write("Hello 1!")
async def say2():
await asyncio.sleep(1)
write("Hello 2!")
write("start")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
say1(),
say2()
))
write("exit")
loop.close()
来源:https://maketips.net/tip/146/parallel-execution-of-asyncio-functions有关带有调用图的代码的详细说明,请参见link。
我一直很难理解 Python 的 asyncio 模块以及如何不阻塞异步调用。例如,给定此代码片段:
import aiohttp
import asyncio
import async_timeout
async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
async def main(loop):
print(1)
async with aiohttp.ClientSession(loop=loop) as session:
html = await fetch(session, 'http://python.org')
print(html)
print(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
我预计,与 Javascript 中的工作方式类似,输出为
1
2
<!doctype html>
<...>
相反,该函数打印 1,阻塞直到它返回 html,然后打印 2。它为什么阻塞,我如何/如何避免阻塞?感谢您的帮助。
你的问题是这一行:
html = await fetch(session, 'http://python.org')
这意味着 - 等待直到从 fetch 获得响应,或者换句话说,它会阻塞 current 协程,直到 fetch 协程 return 的响应文本。
为了让事情更简单:
- 提取操作本身不阻塞,只有等待。
- await 只阻塞当前协程,而不是线程。
- 就像vincent说的那样,调用操作本身并没有做任何事情(我们不启动抓取过程)
Calling a coroutine does not start its code running – the coroutine object returned by the call doesn’t do anything until you schedule its execution. There are two basic ways to start it running: call await coroutine or yield from coroutine from another coroutine (assuming the other coroutine is already running!), or schedule its execution using the ensure_future() function or the AbstractEventLoop.create_task() method.
和修复(以获得所需的输出):
import asyncio
import aiohttp
import async_timeout
async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
async def main(loop):
print(1)
async with aiohttp.ClientSession(loop=loop) as session:
future = asyncio.ensure_future(fetch(session, 'http://python.org')) # we start the fetch process
print(2)
html = await future # we wait for getting the fetch response
print(html)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
为了理解 python 3.6 async def
风格的 asyncio,我建议你 运行 这个简单的例子:
import asyncio
import time
def write(msg):
print(msg, flush=True)
async def say1():
await asyncio.sleep(1)
write("Hello 1!")
async def say2():
await asyncio.sleep(1)
write("Hello 2!")
write("start")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
say1(),
say2()
))
write("exit")
loop.close()
来源:https://maketips.net/tip/146/parallel-execution-of-asyncio-functions有关带有调用图的代码的详细说明,请参见link。