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本质上是基于协程的,其asyncawait语法就是为了实现这样的模式。

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中的异步设计模式,利用yieldawait,用于非阻塞式的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.sleepsocket 等模块的修改,使它们可以与 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 的应用场景

  1. 异步任务执行

    • 允许你将耗时长的任务放在后台运行,例如发送电子邮件、生成报告或处理图像。
  2. 定时任务

    • 使用集成的 celery beat 执行定时任务,使其可以替代一些传统的 cron 作业。
  3. 任务工作流

    • 支持复杂的工作流,允许任务之间有依赖关系。

使用案例:异步秒杀

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

 

posted @   凡人半睁眼  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2021-02-21 nginx热升级

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示