Python:异步比线程有啥优势? [关闭]
Posted
技术标签:
【中文标题】Python:异步比线程有啥优势? [关闭]【英文标题】:Python: what are the advantages of async over threads? [closed]Python:异步比线程有什么优势? [关闭] 【发布时间】:2018-06-09 18:37:12 【问题描述】:我很难理解异步功能如何以及为什么在 python 中工作,但我仍然不确定我是否正确理解了所有内容(尤其是“为什么”部分)。如果我错了,请纠正我。
异步方法和线程的目的都是为了可以同时处理多个任务。
线程方法看起来简单直观。如果python程序同时处理多个任务,我们每个任务都有一个线程(可能带有子线程),每个线程的堆栈反映了相应任务的当前处理阶段。一切都很简单,有易于使用的机制来启动一个新线程并等待它的结果。
据我了解,这种方法的唯一问题是线程很昂贵。
另一种方法是使用async
协程。我可以看到这种方法有几个不便之处。我只会列举其中几个。我们现在有两种方法:常用方法和async
方法。 90% 的时候唯一的区别是你需要记住这个方法是async
并且在调用这个方法时不要忘记使用await
关键字。是的,你不能从普通的方法中调用async
方法。而程序周围所有这些async
- await
语法垃圾只是表明该方法能够将控制权交给消息循环。
线程方法没有所有这些不便。但是async
- await
方法允许处理比线程方法更多的并发任务。这怎么可能?
对于每个并发任务,我们仍然有一个调用堆栈,只是现在它是一个协程调用堆栈。我不太确定,但看起来这是关键区别:通常的堆栈是操作系统堆栈,它们很昂贵,协程堆栈只是一个 python 结构,它们便宜得多。我的理解正确吗?
如果这是正确的,那么将 python 线程/调用堆栈与 OS 线程/调用堆栈解耦以使 python 线程更便宜不是更好吗?
对不起,如果这个问题很愚蠢。我确信选择async
-await
方法有一些原因。只是想了解这些原因。
更新:
对于那些不认为这个问题不好和太宽泛的人。
这是一篇文章Unyielding - 首先解释了为什么线程不好并宣传async
方法。主要论点:线程是邪恶的,很难推断一个可以同时从任意数量的线程执行的例程。
感谢 Nathaniel J. Smith(python Trio library 的作者)提出了这个链接。
顺便说一句,文章中的论点对我来说并不令人信服,但仍然可能有用。
【问题讨论】:
我把它写成评论是因为它没有给出完整的答案:异步是基于协作调度的。您可以确定哪些代码块将不间断地执行。您还可以确保异步代码在不与其他代码交互的情况下运行,因为没有任何东西是并行执行的——与线程不同。这使得同步、锁定和竞争条件等问题更容易处理。 @VPfB 谢谢,好点,我没想到。有趣的是,上周我不得不使用threading.Lock
,即使该应用程序是基于async
的。代码的“关键部分”是与外部应用程序通信,重要的是,来自不同协程的一系列请求不会在时间上重叠。所以即使没有线程,同步问题也不会完全消失。
multiprocessing vs multithreading vs asyncio
【参考方案1】:
This article 回答您的问题。
TL;博士?
由于GIL(全局解释器锁),Python 中的线程效率低下,这意味着多个线程不能像您在多处理器系统上所期望的那样并行运行。另外,您必须依靠解释器在线程之间切换,这会增加效率。
asyc/asyncio 允许在单个线程内并发。作为开发人员,这为您提供了对任务切换的更细粒度的控制,并且可以为并发 I/O 绑定任务提供比 Python 线程更好的性能。
您没有提到的第三种方法是multiprocessing。这种方法使用进程进行并发,并允许程序充分利用多核硬件。
【讨论】:
不,这不是 TL;DR!但是我需要一些时间来阅读这篇文章。感谢您的回答! @lesnik 这回答了你的问题吗? GIL 是个问题,但它没有解释为什么 asyncio 方法比线程更好。它解释了为什么线程并不比 asyncio 好很多。是的,由于 GIL(一点点简化),在给定时刻只能运行一个 python 线程。情况与 asyncio 相同 - 在给定时刻只有一个任务正在运行。还有一个论点:线程调度器(或调度器或选择器)不够好,可以选择在 IO 操作上被阻塞的线程。我不确定这是正确的(请参阅第二个答案的沙丘评论)。这篇文章没有回答我的问题:(。【参考方案2】:Asyncio 是一个完全不同的世界,AFAIK 是 python 对 node.js 的回答,它从一开始就做这件事。例如。这个official python doc 关于异步状态:
异步编程不同于经典的“顺序”编程
因此,您需要决定是否要跳入那个兔子洞并学习这个术语。如果您面临与网络或磁盘相关的繁重任务,这可能才有意义。如果你是,例如this article 声称 python 3 的 asyncio 可能比 node.js 更快并且接近 Go 的性能。
也就是说:我还没有使用 asyncio,所以我不能对此发表评论,但我可以评论你问题中的几句话:
所有这些 async - await 程序周围的语法垃圾只是表明该方法能够将控制权交给消息循环
据我所知,您有一个 asyncio 的初始设置,但是所有调用的语法都比使用线程执行相同的操作要少,您需要 start()
和 join()
并且可能还要检查使用is_alive()
,要获取返回值,您需要先设置一个共享对象。所以:不,asyncio 只是看起来不同,但最终程序很可能看起来比线程更干净。
据我了解,这种方法的唯一问题是线程很昂贵
不是真的。启动一个新线程非常便宜,并且 AFAIK 的成本与在 C 或 Java 中启动“本机线程”的成本相同
看起来这是关键的区别:通常的堆栈是操作系统堆栈,它们很昂贵,协程堆栈只是一个 python 结构,它们便宜得多。我的理解正确吗?
不是真的。没有什么比创建操作系统级线程更好的了,它们很便宜。 asyncio 更擅长的是你需要更少的线程切换。因此,如果您有许多并发线程在等待网络或磁盘,那么 asyncio 可能会加快速度。
【讨论】:
我觉得你对绿色线程有点困惑。尤其是当您在同一句话中提到内核时。 CPython 默认不使用绿色线程(这就是 asyncio、gevent、PyPy 等人的用途)。 CPython 使用 pthreads 或 nt 线程,取决于操作系统。由于 GIL,只有一个线程在解释不会使线程变为绿色(它只是给它们一个类似绿色的属性)。在 CPython 中并行是可能的,它只是无法并行化的 python 字节码。任何发布 GIL 的扩展库,如 numpy,都可以并行运行。 @Dunes,是的,现在我很困惑。我认为 python 确实 将 pthreads 转变为“绿色线程”,它从操作系统中移除调度程序并播放调度程序本身并因此引入 GIL。有错吗? 顺便说一句:现在删除了“绿色线程”段落,因为沙丘很可能是正确的,我在那里搞混了 这很奇怪。调度仍然由操作系统管理。但是,由于 GIL,任何已调度且不持有锁的线程都无法执行任何操作(表现良好的扩展库除外)。因此,操作系统和 CPython 都必须就运行哪个线程达成一致(以及操作系统的随机选择)。这是 GIL 的主要缺点。但是,CPython 中的线程对于 IO 来说是可以的,因为操作系统知道不会调度任何正在等待未完成的 IO 的线程。 (CPython 在系统调用执行实际 IO 之前释放 GIL)。 感谢您的回答,但我不同意您的论点(至少在您比较代码复杂性时)。带有线程的x = await get_x()
的模拟只是x = get_x()
- 你的线程只是阻塞,直到x
的值准备好。实现 smthn 之类的代码的复杂性,例如“启动多个任务并等待它们都准备好”,这两种方法大致相同。以上是关于Python:异步比线程有啥优势? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
使用 QGraphicsWebView 比使用 QWebView 有啥优势?