python 并发整理
一、多进程(Multiprocessing)
1、使用场景
适合CPU密集型任务,因为每个进程运行在独立的Python解释器中,不受全局解释器锁(GIL)的影响。
多个进程可以真正并行地执行,并利用多核处理器的能力。
2、代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from multiprocessing import Process import time def task(): print ( "Task is running" ) time.sleep( 10 ) processes = [] for _ in range ( 50 ): p = Process(target = task) processes.append(p) p.start() for p in processes: p.join() |
同时启动50个进程,50个进程执行同样的任务,他们没有GIL锁,时间几乎是并发的,执行结束10s左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # time 统计的时间 real 0m10 . 135s user 0m0 . 094s sys 0m0 . 179s # 查看进程 ps - ef | grep - w 'process_example.py' | wc - l 52 root 3925498 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925499 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925500 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925501 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925502 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925503 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925504 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925505 3925455 0 15 : 52 pts / 0 00 : 00 : 00 python3 process_example.py root 3925565 3885440 0 15 : 52 pts / 1 00 : 00 : 00 grep - - color = auto process_example.py |
二、多线程(Threading)
1、使用场景
适用于I/O密集型任务,如文件读取、网络请求等。线程共享内存,因此适合需要频繁数据交换的任务。
相比之下,对于CPU密集型任务,由于几乎所有的时间都用于执行Python代码,GIL几乎始终被一个线程所占用,导致其他线程无法并行执行。
然而,在I/O密集型任务中,频繁的I/O操作和GIL的适时释放使线程能够更好地并行工作,从而减轻GIL对性能的负面影响。
因此,在I/O密集型应用场景中,多线程的使用通常能够有效地提高性能,因为阻塞I/O操作的等待时间可以被用来执行其他进程或线程的工作。
2、案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import threading import time def cpu_intensive_task(n): print (f "CPU Thread {n} starting..." ) count = 0 for _ in range ( 50 ): # 重复执行多次耗时任务 # 一个更重的计算任务 for i in range ( 10 * * 7 ): count + = i time.sleep( 1 ) # 增加一些休眠时间 print (f "CPU Thread {n} finished with count = {count}" ) def io_intensive_task(n): print (f "I/O Thread {n} starting..." ) for _ in range ( 50 ): # 模拟一个I/O耗时任务,例如文件读写 time.sleep( 2 ) # 模拟I/O等待时间 print (f "I/O Thread {n} finished." ) threads = [] num_cpu_threads = 4 num_io_threads = 4 # 创建CPU密集型任务的线程 #for i in range(num_cpu_threads): # t = threading.Thread(target=cpu_intensive_task, args=(i,)) # threads.append(t) # t.start() # 创建I/O密集型任务的线程 for i in range (num_io_threads): t = threading.Thread(target = io_intensive_task, args = (i,)) threads.append(t) t.start() for t in threads: t.join() print ( "All threads have completed." ) |
3、进程池和线程池这里不再讨论
1 | https: / / www.cnblogs.com / dgp - zjz / p / 17536048.html |
4、GIL 全局解释器锁(Global Interpreter Lock,简称 GIL)
GIL是一种用于保护 Python 解释器在多线程环境下的数据完整性的机制。GIL 只存在是 CPython 解释器中,即官方的 Python 解释器实现
GIL 是一个互斥锁,你可以使用多线程来并发处理任务,但在同一时刻只能有一个线程在执行 Python 字节码。
1 | https: / / www.cnblogs.com / dgp - zjz / p / 17535159.html |
三、异步编程(asyncio)
1、使用场景
适合处理大量并发I/O操作,如网络应用、服务器编程等。使用单线程事件循环来调度任务。
asyncio
本质上是基于协程的,其async
和await
语法就是为了实现这样的模式。
2、代码
1 2 3 4 5 6 7 8 9 10 11 | import asyncio async def task(): print ( "Task is running" ) await asyncio.sleep( 20 ) # 模拟I/O操作 async def main(): tasks = [task() for _ in range ( 50 )] await asyncio.gather( * tasks) asyncio.run(main()) |
经过测试asyncio 模块没有出现GIL锁占用时间且是一个线程
1 | GIL: 0.00 % , Active: 0.00 % , Threads: 1 |
四、携程(Coroutines)
1、使用场景
协程是Python中的异步设计模式,利用yield
和await
,用于非阻塞式的I/O操作
协程是单线程下的并发,它是程序员级别的,我们来控制如何切换。
进程的开销 >>>>>> 线程的开销 >>>>>> 协程的开销
协程的使用需要借助于第三方模块 gevent 模块或者 asyncio 模块
2、案例(这里介绍gevent的方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import gevent from gevent import monkey import time # Patch all to make the standard library cooperative monkey.patch_all() def task(task_id): print (f "Task {task_id} started." ) # 模拟一个耗时操作 time.sleep( 1 ) print (f "Task {task_id} completed." ) def main(): # 创建多个任务 tasks = [gevent.spawn(task, i) for i in range ( 5 )] # 并发地运行所有任务 gevent.joinall(tasks) if __name__ = = '__main__' : main() |
补充说明
monkey.patch_all()
:
将标准库的一些阻塞操作打补丁,使其在 gevent
的绿色线程环境中变成非阻塞的。这包含对 time.sleep
、socket
等模块的修改,使它们可以与 gevent
的事件循环协作。
猴子补丁它会将(socket、select、threading 等)这些模块的阻塞式 I/O 操作替换为非阻塞的版本
gevent.spawn(task, i)
:
gevent.spawn
用于启动新的 greenlet,这是 gevent
中的轻量级协程。每个任务会异步地运行而不阻塞其他任务。
gevent.joinall(tasks)
:
等待所有的 gevent
协程完成,可以理解为同步等待所有并发任务完成。
https://www.cnblogs.com/dgp-zjz/p/17535797.html
五、celery
分布式任务队列库
它主要用于处理实时请求与任务调度,而不是直接用于运行异步代码。celery
允许你在生产环境中分发一组任务,以便通过消息中间件(如 RabbitMQ 或 Redis)与不同的工作节点进行通信。
异步执行:celery
提供了一种优雅的方式将任务异步地推送到后台,不阻塞主线程。
横向扩展:可以通过增加更多的 worker 实例来扩展处理能力。
与 Web 框架的集成:伴随 Flask、Django 等框架,celery
也有根据框架定制的插件或使用指南。
celery
的应用场景
-
异步任务执行:
- 允许你将耗时长的任务放在后台运行,例如发送电子邮件、生成报告或处理图像。
-
定时任务:
- 使用集成的
celery beat
执行定时任务,使其可以替代一些传统的 cron 作业。
- 使用集成的
-
任务工作流:
- 支持复杂的工作流,允许任务之间有依赖关系。
使用案例:异步秒杀
1 | https: / / www.cnblogs.com / dgp - zjz / p / 17773173.html |
六、性能分析工具
1、py-spy
功能:用于对正在运行的Python程序进行采样分析(sampling profiler),可以实时监控CPU的使用情况。
2、用法
1 2 3 | # pip install py-spy py - spy top - - pid <PID> |
2、memray
1、功能
memray是一款高效的内存分析工具,用于检测Python程序的内存使用情况及潜在的内存泄漏
2、用法
1 2 3 | # pip install memray memray run script.py |