【发布时间】:2019-08-03 14:04:00
【问题描述】:
我有以下简化代码:
async def asynchronous_function(*args, **kwds):
statement = await prepare(query)
async with conn.transaction():
async for record in statement.cursor():
??? yield record ???
...
class Foo:
def __iter__(self):
records = ??? asynchronous_function ???
yield from records
...
x = Foo()
for record in x:
...
上面的???不知道怎么填。我想产生记录数据,但是如何包装异步代码真的不明显。
【问题讨论】:
-
异步代码和阻塞代码混用通常是个坏主意,可以把
for record in x换成async for record in x吗? -
问题是,一旦我有了异步,我必须把它一直推到堆栈上——我不想重写我的所有堆栈以符合异步的风格。或者换一种说法,我让这段代码在没有异步的情况下工作,但我想试试异步代码,看看它是否更有性能。我看到的所有例子都是玩具例子......
-
嗯,asyncio 通常不提供性能。虽然它确实提供了协作多任务处理,但是您通常必须对每个阻塞调用使用 async/await 范例才能看到好处。而在这种情况下,
for record in x:确实是一个阻塞调用。 -
Asyncio 更多的是关于可扩展性而不是性能。使用线程完全可以与 50 个同伴交谈;与 500 或 5000 交谈将是一个问题,因为您要么必须生成大量 OS 线程(并调试它们之间的争用问题,尤其是与 GIL 结合使用),要么使用线程池并花费非生产时间等待空闲插槽在游泳池。 Asyncio 允许您一次处理多个连接,而不需要每个连接一个 OS 线程,同时通过协程保留可读代码。有关在非异步程序中使用 asyncio 的示例,请参阅我的答案。
-
也许可以在这里搭载 cmets。我觉得这里的命名法正在使我们变得更好。当我提到性能时,我真的在考虑并发的关键方面,即我花费时钟周期空闲等待 I/O。我有独立的工作,如果我有办法释放它们,肯定可以使用这些时钟周期。我认为 asyncio 可以做到这一点,但是在与当前同步代码交互时它非常笨重。