目录
一、多任务基础:并发与并行
1. 什么是多任务
2. 两种表现形式
二、进程:操作系统资源分配的最小单位
1. 进程的概念
2. 多进程实现多任务
2.1 基础示例:边听音乐边敲代码
2.2 带参数的进程任务
2.3 进程编号与应用注意点
2.3.1 进程编号的作用
2.3.2 关键注意点
三、线程:程序执行的最小单位
1. 线程的概念
2. 多线程实现多任务
3. 线程的核心特性
四、进程与线程的对比
五、协程:用户态的轻量级线程
1. 协程的概念
2. 核心优势
3. 基于asyncio实现协程
4. 协程的适用场景与注意事项
六、总结:如何选择多任务方案
在日常开发中,我们经常会遇到需要同时处理多个任务的场景,比如网盘同时下载多个文件、程序一边播放音乐一边执行计算。这些都离不开多任务编程技术。本文将基于 Python,从多任务的基础概念出发,详细讲解进程、线程与协程三种实现方式,帮助你轻松掌握多任务编程的核心逻辑。
一、多任务基础:并发与并行
1. 什么是多任务
多任务指在同一时间内执行多个任务,比如电脑同时运行微信、浏览器和代码编辑器。其核心价值是充分利用 CPU 资源,提升程序执行效率。
2. 两种表现形式
多任务的实现分为 “并发” 和 “并行”,二者的核心区别在于是否 “真正同时执行”:
(1)并发:单核 CPU 场景下,操作系统轮流让多个任务交替执行(如任务 1 执行 0.01 秒→切换到任务 2 执行 0.01 秒)。由于 CPU 速度极快,表面上看起来任务在同时运行。适用场景:任务数量>CPU 核心数。
(2)并行:多核 CPU 场景下,每个核心分别执行一个任务,多个任务真正同时进行。适用场景:任务数量≤CPU 核心数。
二、进程:操作系统资源分配的最小单位
1. 进程的概念
进程(Process)是操作系统进行资源分配和调度的基本单位,一个正在运行的程序就是一个进程(如打开的微信、Chrome 浏览器)。一个程序运行后至少会创建一个主进程。
2. 多进程实现多任务
通过 Python 的multiprocessing模块,我们可以创建子进程,让多个任务并行执行。
核心步骤
(1)导入multiprocessing模块;
(2)用multiprocessing.Process()创建进程对象,指定目标任务(函数名);
(3)调用start()方法启动进程。
2.1 基础示例:边听音乐边敲代码
import multiprocessing
import time# 任务1:听音乐
def music():for i in range(3):print("听音乐...")time.sleep(0.2)# 任务2:敲代码
def coding():for i in range(3):print("敲代码...")time.sleep(0.2)if __name__ == '__main__':# 创建进程对象music_process = multiprocessing.Process(target=music)coding_process = multiprocessing.Process(target=coding)# 启动进程music_process.start()coding_process.start()
2.2 带参数的进程任务
通过args(元组)或kwargs(字典)给任务传递参数:
if __name__ == '__main__':# 元组传参(注意逗号)music_process = multiprocessing.Process(target=music, args=(3,))# 字典传参coding_process = multiprocessing.Process(target=coding, kwargs={'count': 3})music_process.start()coding_process.start()
2.3 进程编号与应用注意点
2.3.1 进程编号的作用
每个进程都有唯一编号(PID),用于区分主进程和子进程,方便进程管理。通过os
模块获取:
(1)os.getpid():获取当前进程编号;
(2)os.getppid():获取父进程编号(创建当前进程的进程)
。
2.3.2 关键注意点
(1)进程间不共享全局变量:子进程会拷贝主进程的资源,修改子进程的全局变量不会影响主进程;
(2)主进程与子进程的结束顺序:默认主进程会等待所有子进程执行完再结束。若需主进程结束时子进程也终止,可:
- 设置守护进程:process.daemon = True;
- 手动终止子进程:process.terminate()。
三、线程:程序执行的最小单位
1. 线程的概念
线程(Thread)是进程内的执行单元,共享进程的所有资源(如内存、文件句柄)。一个进程默认有一个主线程,可创建多个子线程,就像 “一个 QQ 打开两个聊天窗口”,既实现多任务又节省资源。
2. 多线程实现多任务
通过threading
模块实现,步骤与多进程类似,且资源开销更低、启动速度更快。
基础示例
import threading
import timedef music():for i in range(3):print("听音乐...")time.sleep(0.2)def coding():for i in range(3):print("敲代码...")time.sleep(0.2)if __name__ == '__main__':# 创建线程对象music_thread = threading.Thread(target=music)coding_thread = threading.Thread(target=coding)# 启动线程music_thread.start()coding_thread.start()
3. 线程的核心特性
- 共享全局变量:同一进程内的线程共享全局变量(如列表、字典),但需注意 “线程安全”(需用锁避免数据竞争);
- 执行顺序无序:线程的执行由 CPU 调度决定,无法保证顺序;
- 守护线程:设置
thread.daemon = True
,主线程结束时子线程自动终止。
四、进程与线程的对比
特性 | 进程(Process) | 线程(Thread) |
---|---|---|
定义 | 资源分配最小单位 | 程序执行最小单位 |
资源开销 | 高(独立内存、资源) | 低(共享进程资源) |
启动速度 | 慢 | 快 |
全局变量共享 | 不共享(需 IPC 通信) | 共享(需锁保证安全) |
适用场景 | CPU 密集型任务(如计算、数据分析) | I/O 密集型任务(如网络请求、文件读写) |
模块 | multiprocessing | threading |
五、协程:用户态的轻量级线程
1. 协程的概念
协程(Coroutine)是用户态的轻量级线程,通过 “协作式调度” 实现并发 —— 无需操作系统介入,仅需保存寄存器上下文,切换效率极高。
2. 核心优势
- 无锁机制:避免多线程的同步开销;
- 高并发:单线程内可处理数千个 I/O 密集型任务;
- 代码简洁:用
async/await
语法,以同步风格写异步逻辑。
3. 基于asyncio实现协程
asyncio是 Python 官方协程库,核心是 “事件循环”(管理协程的执行)。
基础示例
import asyncio# 定义协程函数(用async修饰)
async def my_coroutine():print("Start")# 非阻塞等待(替代time.sleep,不阻塞事件循环)await asyncio.sleep(1)print("End")# 启动事件循环
asyncio.run(my_coroutine())
并发执行多个协程
用asyncio.create_task()创建任务,asyncio.gather()并发执行:
async def task(name, delay):await asyncio.sleep(delay)print(f"{name} completed")async def main():# 创建任务列表tasks = [asyncio.create_task(task("A", 2)),asyncio.create_task(task("B", 1))]# 并发执行await asyncio.gather(*tasks)asyncio.run(main())
# 输出:B completed → A completed(按完成时间排序)
4. 协程的适用场景与注意事项
- 适用场景:I/O 密集型任务(如网络爬虫、API 调用),结合aiohttp(异步 HTTP 库)可大幅提升效率;
- 注意事项:
(1)协程内禁用time.sleep()等同步操作,需用asyncio.sleep();
(2)需 Python 3.7+,推荐用asyncio.run()管理事件循环。
(3)不适合 CPU 密集型任务(需结合多进程);
六、总结:如何选择多任务方案
- CPU 密集型任务(如计算、矩阵运算):用多进程(绕过 Python GIL 锁,利用多核 CPU);
- I/O 密集型任务(如网络请求、文件读写):优先用协程(效率最高、资源开销最低),其次用多线程;
- 简单多任务场景:若无需多核利用,用多线程(实现简单、通信方便)。