文章目录
- 一、进程和线程的比较
- 1. 定义
- 2. 内存空间
- 3. 创建和销毁
- 4. 并发性
- 5. 稳定性
- 6. 通信
- 7. Python中的实现
- 8. 示例代码
- 进程示例
- 线程示例
- 小结
- 二、协程
- 协程的关键特性
- Python 中的协程
- 示例
- 结果
- 协程的工作原理
- 协程与生成器的关系
- 协程的适用场景
- 示例:并发执行多个协程
- 执行一
- 返回一
- 执行二
- 返回二
- 小结
一、进程和线程的比较
线程和进程是并发执行的两种主要方式
1. 定义
- 进程:进程是操作系统分配资源的基本单位,每个进程都有独立的内存空间,包含代码、数据和系统资源。进程之间相互隔离,一个进程崩溃不会影响其他进程。
- 线程:线程是进程内的执行单元,一个进程可以包含多个线程。线程共享进程的内存空间和资源,因此线程间的通信比进程间更高效,但也更容易出现数据竞争等问题。
2. 内存空间
- 进程:每个进程有独立的内存空间,进程间通信需要通过IPC(Inter-Process Communication)机制,如管道、消息队列、共享内存等。
- 线程:线程共享进程的内存空间,线程间可以直接访问共享数据,但需要同步机制(如锁)来避免竞争条件。
3. 创建和销毁
- 进程:创建和销毁进程的开销较大,因为需要分配和回收独立的内存空间和资源。
- 线程:创建和销毁线程的开销较小,因为它们共享进程的资源。
4. 并发性
- 进程:由于进程间相互独立,多进程程序可以充分利用多核CPU,适合CPU密集型任务。
- 线程:线程适合I/O密集型任务,但由于Python的GIL(全局解释器锁),多线程在CPU密集型任务中无法充分利用多核CPU。
5. 稳定性
- 进程:进程间相互隔离,一个进程崩溃不会影响其他进程,稳定性较高。
- 线程:线程共享内存空间,一个线程崩溃可能导致整个进程崩溃,稳定性较低。
6. 通信
- 进程:进程间通信需要通过IPC机制,较为复杂。
- 线程:线程间通信可以直接通过共享内存,较为简单,但需要同步机制。
7. Python中的实现
- 进程:Python中使用
multiprocessing
模块来创建和管理进程。 - 线程:Python中使用
threading
模块来创建和管理线程。
8. 示例代码
进程示例
import multiprocessingdef worker():print("Process worker")if __name__ == "__main__":p = multiprocessing.Process(target=worker)p.start()p.join()
线程示例
import threadingdef worker():print("Thread worker")t = threading.Thread(target=worker)
t.start()
t.join()
小结
- 进程适合需要高隔离性和CPU密集型任务。
- 线程适合I/O密集型任务和需要高效通信的场景。
选择使用进程还是线程,取决于具体的应用场景和需求。
二、协程
参考1:https://liaoxuefeng.com/books/python/async-io/coroutine/index.html
参考2:https://liaoxuefeng.com/books/python/async-io/asyncio/index.html
协程(Coroutine) 是一种特殊的函数,能够在执行过程中暂停(挂起)并在稍后恢复执行。它是实现异步编程的核心机制之一,常用于处理 I/O 密集型任务(如网络请求、文件读写等),避免阻塞主线程。
协程与普通函数的区别在于:
- 普通函数从开始执行到结束,不会中途暂停。
- 协程可以在执行过程中暂停(通过
await
或yield
),并在适当的时候恢复执行。
协程的关键特性
-
可暂停和恢复
协程可以在执行过程中暂停,将控制权交还给调用者,稍后再从暂停的地方恢复执行。 -
异步非阻塞
协程通常与异步 I/O 操作结合使用,避免阻塞主线程,提高程序的并发性能。 -
轻量级
协程的上下文切换开销比线程小得多,适合高并发场景。
Python 中的协程
在 Python 中,协程通过 async
和 await
关键字实现:
async def
:定义一个协程函数。await
:暂停协程的执行,等待异步操作完成。
示例
import asyncioasync def say_hello():print("Hello")await asyncio.sleep(1) # 模拟 I/O 操作,暂停 1 秒print("World")# 运行协程
asyncio.run(say_hello())
结果
Hello!
(等待约1秒)
World!
协程的工作原理
-
事件循环(Event Loop)
协程的运行依赖于事件循环。事件循环负责调度协程的执行,当协程遇到await
时,事件循环会暂停当前协程,转而执行其他任务。 -
挂起和恢复
当协程遇到await
时,它会挂起自己,并将控制权交还给事件循环。事件循环会在异步操作完成后恢复协程的执行。
协程与生成器的关系
协程的实现基于 Python 的生成器(Generator)。生成器通过 yield
关键字实现暂停和恢复,而协程通过 await
实现类似的功能。不过,协程更专注于异步编程,而生成器更常用于惰性计算。
协程的适用场景
- I/O 密集型任务
如网络请求、数据库操作、文件读写等。 - 高并发应用
如 Web 服务器、爬虫、实时数据处理等。 - 异步编程
需要避免阻塞主线程的场景。
示例:并发执行多个协程
import asyncioasync def io_operation(name, delay):print(f"IO操作 {name} 开始")await asyncio.sleep(delay)print(f"IO操作 {name} 完成")return f"{name} 的结果"async def compute_operation(name, delay):print(f"计算操作 {name} 开始")await asyncio.sleep(delay) # 模拟耗时计算print(f"计算操作 {name} 完成")return f"{name} 的计算结果"async def network_operation(name, delay):print(f"网络操作 {name} 开始")await asyncio.sleep(delay) # 模拟网络请求print(f"网络操作 {name} 完成")return f"{name} 的网络响应"# 设计task中有多个协程
async def task(name, delay):print(f"Task {name} 启动")# 并发执行多个异步操作results = await asyncio.gather(io_operation(f"{name}-IO", delay),compute_operation(f"{name}-Compute", delay/2),network_operation(f"{name}-Network", delay*1.5))# 串行执行一些操作result2 = await io_operation(f"{name}-Sequential", 10) # delay/3result3 = await compute_operation(f"{name}-Final", delay/4)print(f"Task {name} 完成,获得结果:{results}, {result2}, {result3}")
执行一
async def main():# 并发执行多个协程await asyncio.gather(task("A", 2),task("B", 1),task("C", 3),)
if __name__ == "__main__":asyncio.run(main())
返回一
Task A 启动
Task B 启动
Task C 启动
IO操作 A-IO 开始
计算操作 A-Compute 开始
网络操作 A-Network 开始
IO操作 B-IO 开始
计算操作 B-Compute 开始
网络操作 B-Network 开始
IO操作 C-IO 开始
计算操作 C-Compute 开始
网络操作 C-Network 开始
计算操作 B-Compute 完成
计算操作 A-Compute 完成
IO操作 B-IO 完成
网络操作 B-Network 完成
计算操作 C-Compute 完成
IO操作 B-Sequential 开始
IO操作 A-IO 完成
网络操作 A-Network 完成
IO操作 C-IO 完成
IO操作 A-Sequential 开始
网络操作 C-Network 完成
IO操作 C-Sequential 开始
IO操作 B-Sequential 完成
计算操作 B-Final 开始
计算操作 B-Final 完成
Task B 完成,获得结果:['B-IO 的结果', 'B-Compute 的计算结果', 'B-Network 的网络响应'], B-Sequential 的结果, B-Final 的计算结果
IO操作 A-Sequential 完成
计算操作 A-Final 开始
计算操作 A-Final 完成
Task A 完成,获得结果:['A-IO 的结果', 'A-Compute 的计算结果', 'A-Network 的网络响应'], A-Sequential 的结果, A-Final 的计算结果
IO操作 C-Sequential 完成
计算操作 C-Final 开始
计算操作 C-Final 完成
Task C 完成,获得结果:['C-IO 的结果', 'C-Compute 的计算结果', 'C-Network 的网络响应'], C-Sequential 的结果, C-Final 的计算结果
执行二
async def main():tasks = [task("A", 2), task("B", 1), task("C", 3)]for i in range(3):print(f"Execute Task {i} started")task_return = asyncio.create_task(tasks[i % len(tasks)])await asyncio.wait_for(task_return, timeout=300)print(f"Execute Task {i} completed")if __name__ == "__main__":asyncio.run(main())
返回二
Execute Task 0 started
Task A 启动
IO操作 A-IO 开始
计算操作 A-Compute 开始
网络操作 A-Network 开始
计算操作 A-Compute 完成
IO操作 A-IO 完成
网络操作 A-Network 完成
IO操作 A-Sequential 开始
IO操作 A-Sequential 完成
计算操作 A-Final 开始
计算操作 A-Final 完成
Task A 完成,获得结果:['A-IO 的结果', 'A-Compute 的计算结果', 'A-Network 的网络响应'], A-Sequential 的结果, A-Final 的计算结果
Execute Task 0 completed
Execute Task 1 started
Task B 启动
IO操作 B-IO 开始
计算操作 B-Compute 开始
网络操作 B-Network 开始
计算操作 B-Compute 完成
IO操作 B-IO 完成
网络操作 B-Network 完成
IO操作 B-Sequential 开始
IO操作 B-Sequential 完成
计算操作 B-Final 开始
计算操作 B-Final 完成
Task B 完成,获得结果:['B-IO 的结果', 'B-Compute 的计算结果', 'B-Network 的网络响应'], B-Sequential 的结果, B-Final 的计算结果
Execute Task 1 completed
Execute Task 2 started
Task C 启动
IO操作 C-IO 开始
计算操作 C-Compute 开始
网络操作 C-Network 开始
计算操作 C-Compute 完成
IO操作 C-IO 完成
网络操作 C-Network 完成
IO操作 C-Sequential 开始
IO操作 C-Sequential 完成
计算操作 C-Final 开始
计算操作 C-Final 完成
Task C 完成,获得结果:['C-IO 的结果', 'C-Compute 的计算结果', 'C-Network 的网络响应'], C-Sequential 的结果, C-Final 的计算结果
Execute Task 2 completed
小结
协程是一种轻量级的并发编程工具,能够暂停和恢复执行,适合处理 I/O 密集型任务和高并发场景。在 Python 中,协程通过 async
和 await
实现,是异步编程的核心机制之一。
∼ O n e p e r s o n g o f a s t e r , a g r o u p o f p e o p l e c a n g o f u r t h e r ∼ \sim_{One\ person\ go\ faster,\ a\ group\ of\ people\ can\ go\ further}\sim ∼One person go faster, a group of people can go further∼