Python中使用线程和async/await的区别是什么?

4
我正在尝试在Python3中更熟悉asyncio的用法,但我不知道何时应该使用async/await或线程。它们之间有区别吗?或者其中一个比另一个更容易使用。

例如,在这两个函数之间,有一个更好的选择吗?

只是普通代码。

def func1()
def func2()

threads = [threading.Thread(target=func1), threading.Thread(target=func2)]
for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

对比

async def func1()
async def func2()

await asyncio.gather(func1(), func2())

1
这个由核心开发者Raymond Hettinger制作的视频将回答你所有的问题;https://youtu.be/9zinZmE3Ogk - SteveJ
2个回答

5

我的建议是在所有情况下都使用线程,除非你正在编写一个高性能的并发应用程序,并且基准测试表明仅使用线程太慢了。

原则上,协程有很多优点。它们更容易理解,因为每个非阻塞操作序列都可以被视为原子(至少如果不与线程组合在一起);而且由于它们很便宜,你可以轻松启动它们以实现更好的关注分离,而不必担心影响性能。

然而,在Python中实际实现协程是一团糟。最大的问题是每个可能阻塞的函数,或者调用任何可能阻塞的代码的函数,或者调用任何可能调用任何可能阻塞的代码的代码的函数,都必须使用asyncawait关键字进行重写。这意味着,如果你想使用一个阻塞的库,或者调用回调进入你的代码并希望在回调中阻塞,那么你根本无法使用该库。由于这个原因,在CPython发行版中现在有许多库的重复副本,甚至包括内置语法(如async for等),但你不能期望大多数通过pip提供的模块都维护两个版本。

线程没有这个问题。如果一个库不是设计为线程安全的,你仍然可以在多线程程序中使用它,要么将其使用限制在一个线程中,要么在所有使用情况下都使用锁进行保护。

因此,尽管存在问题,线程在Python中总体上更容易使用。

有一些第三方协程解决方案可以避免“异步感染”问题,例如greenlet,但除非它们专门设计用于与协程一起工作,否则你仍然无法在内部阻塞的库中使用它们。


我同意,只要你的线程在执行I/O绑定操作时不持有锁(这将阻止其他需要获取相同锁的线程运行),那么它将会和确保底层模块中没有阻塞代码一样快,并且无需处理。 - Steve Freed

1
使用asyncio,代码可以通过await重新获得控制权。在线程中,这由Python调度程序处理。多线程实现了锁定机制以防止共享内存问题。多线程的另一个优点是它能够使用多个核心。
如果您想阅读更多信息,请参阅以下文章: https://medium.com/@nhumrich/asynchronous-python-45df84b82434

多线程在Python中无法使用多个核心。 - user

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接