Python 多并发分析

志扬工作室

共 2761字,需浏览 6分钟

 ·

2023-09-18 23:49

 文章所涉及内容更多来自网络,在此声明,并感谢知识的贡献者!


线程与进程


线程与进程

- 操作系统中,进程是资源分配拥有的单位,线程是调度执行的单位。
- 线程是比进程更小的一个运行单位,一个进程可以有多个线程,这些线程共享该进程所拥有的资源。
线程的出现主要是解决“同数据区同时处理多请求”,如果使用多个进程,通信同步切换都太复杂,因此操作系统的设计者把进程的两个属性分开,即作为调度执行的基本单位,不再是资源分配拥有的单位。
多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响。而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,需要格外强调互斥和同步。
任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。正是由于GIL的存在,python多线程不能利用多核CPU,不过多进程可以,每个进程有自己的GIL锁,有自己的变量,当然多进程带来的切换更耗时。
-多线程相比于单线程的意义:
在计算密集型(如:矩阵运算)任务上,python多线程性能堪忧;
I/O密集(读写磁盘)任务,才是python多线程的用武之地。因为I/O操作,就会释放GIL锁,不会影响性能
总之python多线程适合请求量很大,但单个请求处理时间短的任务;多进程适合处理计算密集型任务。

多处理库

多处理库:
concurrenct.futures,ProcessPoolExecutor() ,ThreadPoolExecutor() , asyncio
threading: 操作系统级线程的接口。受 CPU 限制的工作主要由GIL序列化,因此不要指望您的计算会加速。当您需要并行调用阻塞 API 时使用它,特别是当您需要控制线程创建时。避免创建太多线程,因为它们很昂贵。
multiprocessing: 生成多个 python 进程的接口,其 API 有意类似于threading. 多个进程并行工作,因此您实际上可以使用此方法加快计算速度。缺点是您不能在不使用多处理特定工具的情况下共享内存中的数据结构。
concurrent.futures:一个更现代的界面threading和multiprocessing,它提供了方便的线程/进程池,它调用执行者。池的主要入口点是submit返回句柄的方法,您可以测试该句柄是否完成或等待其结果。获取结果将为您提供提交函数的返回值并正确传播引发的异常(如果有),这对于threading. 在考虑基于线程或进程的并行性时,concurrent.futures 应该是首选工具。
asyncio:虽然之前的选项在提供非阻塞API的意义上是“异步的” (这是apply_async和其他人所指的),但他们仍然依赖线程/进程池来发挥他们的魔力,并且不能真正做更多的事情平行于他们在池中的工人。Asyncio 全面使用单线程执行和异步系统调用。它根本没有阻塞调用,唯一的阻塞部分是asyncio.run()入口点。Asyncio 代码通常使用协程编写,协程使用await暂停直到有趣的事情发生。(挂起与阻塞不同,它允许事件循环线程在您等待时继续执行其他操作。)与基于线程的解决方案相比,它具有许多优点,例如能够产生数千个廉价的“任务”而无需使系统陷入困境,并且能够取消任务或轻松地同时等待多个事情。Asyncio 应该是服务器和连接到多个服务器的客户端的首选工具。
在 asyncio 和多线程/多处理之间进行选择时,请考虑“线程用于并行工作,而异步用于并行等待”的格言。

concurrent.futures简介


concurrent.futures简介
concurrent.futures是3.2引入的新库,在python的多线程threading、多进程multiprocesssing上进一步封装,实现了进程池和线程池。与multiprocessing.Pool(进程池)不同的是,concurrent.futures实现的都是异步操作。
concurrent.futures主要实现了进程池和线程池,适合做派生一堆任务,异步执行完成后,再收集这些任务,且保持相同的api,池的引入带来了一定好处:
- 程序开发更快,代码简洁,调调函数
- 进程线程复用,减去了大量开辟,删除进程线程时的开销
- 有效避免了因为创建进程线程过多,而导致负荷过大的问题
concurrent.futures是重要的异步编程库。内部实现机制非常复杂,简单来说就是开辟一个固定大小为n的进程池/线程池。进程池中最多执行n个进程/线程,当任务完成后,从任务队列中取新任务。若池满,则排队等待。

asynico异步

Asyncio
协程
协程是在用户态实现的上下文切换技术,因此操作系统不知道协程的存在。那么什么是上下文,简单来说就是栈、寄存器、命名空间等等。线程、进程就是操作系统帮你实现的上下文切换技术,你无需关心进程调度。
线程缺点:
线程切换开销太大(相比进程还算小);为了同步互斥,线程要加信号量、锁机制,性能下降;线程不够灵活,操作系统持续地所有线程共享CPU时间,不管线程是否阻塞,出现忙等现象。
协程机制:
相比线程切换,协程上下文切换代价更小。
协程是单线程的,不存在多线程互斥访问共享变量,不用锁、信号量等机制
协程非常灵活,当出现I/O阻塞时,就去切换任务,I/O完成再唤醒,这就是所谓的异步I/O,也就是避免了操作系统书上常说的cpu计算快,磁盘存取慢导致的速度不匹配。
简单概括,协程是为了代替线程而出现的。在用户态,实现的上下文切换技术,只能跑在单线程上。
协程只能跑在单线程,这是为了避免互斥访问共享变量做的妥协。因此协程不适合做计算密集型任务,它适合做I/O密集型任务,asyncio就是异步I/O,讲的就是I/O密集任务。

参考资料

参考资料:
https://qa.1r1g.com/sf/ask/4294629111/
https://zhuanlan.zhihu.com/p/438107406
多线程
https://zhuanlan.zhihu.com/p/437934916
多进程
https://zhuanlan.zhihu.com/p/438551087
concurrent.futures
https://zhuanlan.zhihu.com/p/438627177
asyncio
https://zhuanlan.zhihu.com/p/438789871

浏览 191
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报