文章中"I'm not feeling the async pressure" Armin Ronacher 做出以下观察:
In threaded code any function can yield. In async code only async functions can. This means for instance that the writer.write method cannot block.
此观察是引用以下代码示例得出的:
from asyncio import start_server, run
async def on_client_connected(reader, writer):
while True:
data = await reader.readline()
if not data:
break
writer.write(data)
async def server():
srv = await start_server(on_client_connected, '127.0.0.1', 8888)
async with srv:
await srv.serve_forever()
run(server())
我不明白这个评论。具体来说:
- 为什么同步函数在异步函数内部时无法
yield
? yield
与阻塞执行有什么关系?为什么一个函数不能yield
,就不能阻塞?
最佳答案
逐行进行:
In threaded code any function can yield.
机器上运行的程序是按照进程来组织的。每个进程可能有一个或多个线程。线程与进程一样,由操作系统调度(并且可中断)。在这种情况下,“yield”一词的意思是“让其他代码运行”。当工作在多个线程之间分割时,函数很容易“屈服”:操作系统挂起在一个线程中运行的代码,在另一个线程中运行一些代码,挂起该代码,返回,然后在第一个线程上再工作一些,等等在。通过这种方式在线程之间切换,就实现了并发。
在此执行模型中,被挂起的代码是同步还是异步并不重要。线程内的代码是逐行运行的,因此同步函数的基本假设——运行一行代码与下一行代码之间没有发生任何变化——是没有违反。
In async code only async functions can.
在此上下文中,“异步代码”是指单线程应用程序,它与多线程应用程序执行相同的工作,不同之处在于它通过使用线程内的异步函数来实现并发,而不是使用线程内的异步函数。在不同线程之间分配工作。在此执行模型中,您的解释器(而不是操作系统)负责根据需要在函数之间进行切换以实现并发。
在此执行模型中,在位于异步函数内部的同步函数中间挂起工作是不安全的。这样做意味着在运行同步函数的过程中运行一些其他代码,从而打破了同步函数所做的“逐行”假设。
因此,解释器只会在同步子函数之间等待暂停异步函数的执行,而不会在同步子函数内等待。这就是异步代码中的同步函数不能产生的意思:一旦同步函数开始运行,它就必须完成。
This means for instance that the writer.write method cannot block.
writer.write 方法是同步的,因此在异步程序中运行时是不间断的。如果此方法被阻塞,它不仅会阻塞其内部运行的异步函数,还会阻塞整个程序。那会很糟糕。 writer.write 通过写入写入缓冲区并立即返回来避免阻塞程序。
严格来说,writer.write
可以阻塞,但不建议这样做。
如果您需要在异步函数内部阻塞,正确的方法是等待
另一个异步函数。这就是例如await writer.drain()
会这样做。这将异步阻塞:虽然此特定函数保持阻塞状态,但它将正确让步给可以运行的其他函数。
关于python - 为什么只有异步函数才能在异步代码中产生?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59557125/