一、线程基础概念
1.1 什么是线程
线程(Thread)是程序中的独立执行流,允许程序同时执行多个任务。Python 的 threading 模块提供了跨平台一致的线程管理接口。
1.2 创建线程的两种方式
方式一:直接传入可调用对象(推荐)
import threadingdef worker(num): print(f"Worker {num}")thread = threading.Thread(target=worker, args=(1,))thread.start()thread.join()
方式二:继承 Thread 类
class MyThread(threading.Thread): def run(self): print("Thread running")thread = MyThread()thread.start()
1.3 守护线程(Daemon Thread)
守护线程是一种后台运行的线程,当程序中只剩下守护线程时,Python 程序会自动退出。
# 设置守护线程thread = threading.Thread(target=func, daemon=True)thread.start()
特点:
二、数据竞争(Race Condition)
2.1 什么是竞态条件
当多个线程同时访问和修改共享资源时,最终结果依赖于线程执行的顺序,导致数据不一致。
2.2 实际案例
import threadingcounter = 0def add(): global counter for _ in range(100000): counter += 1 # 非原子操作,存在竞态条件threads = [threading.Thread(target=add) for _ in range(2)]for t in threads: t.start()for t in threads: t.join()print(counter) # 结果可能小于 200000
三、锁(Lock)机制
3.1 锁的基本概念
锁是一种同步原语,用于保护共享资源,确保同一时间只有一个线程可以访问该资源。
锁的两种状态:
- 锁定(locked): 线程正在使用共享资源
- 未锁定(unlocked): 资源空闲,可被线程获取
3.2 锁的核心原理
互斥锁(Mutual Exclusion Lock)确保在同一时刻只有一个线程可以进入临界区(Critical Section)——即访问共享资源的代码段。
3.3 锁的使用方法
方法一:with 语句(推荐)
lock = threading.Lock()counter = 0def safe_add(): global counter with lock: # 自动 acquire() 和 release() counter += 1
方法二:手动 acquire() / release()
lock = threading.Lock()lock.acquire()try: counter += 1 # 临界区代码finally: lock.release() # 确保锁释放
推荐使用with语句,因为更安全、更简洁,不容易忘记释放锁。
3.4 锁的阻塞机制
- 当锁处于"未锁定"状态:调用
acquire() 的线程会立即获得锁 - 当锁处于"锁定"状态:其他线程调用
acquire() 时会被阻塞等待,直到持有锁的线程释放锁
3.5 非阻塞获取锁
if lock.acquire(blocking=False): # 不阻塞,立即返回 try: pass finally: lock.release()
四、锁的注意事项与最佳实践
4.1 避免死锁
死锁通常由以下原因导致:
预防措施:
4.2 最小化锁的持有时间
# 不好的写法:持有锁时间过长with lock: do_something() do_other_thing() time.sleep(2) # 不必在锁内# 更好的写法with lock: update_shared_data()time.sleep(2) # 锁外执行
4.3 锁的代价
五、可重入锁(RLock)
5.1 RLock 的特点
可重入锁(Reentrant Lock)允许同一线程多次获取锁,内部维护计数器。
核心特性:
- 同一线程
acquire() 多少次,需 release() 多少次
5.2 使用场景
适用于递归函数或同一线程需多次进入临界区的场景:
lock = threading.RLock()def recursive_function(): with lock: print("进入临界区") recursive_function() # 同一线程可以再次获取锁
六、其他同步机制
6.1 信号量(Semaphore)
用于控制同时访问共享资源的线程数量:
semaphore = threading.Semaphore(2) # 允许最多2个线程同时访问def access_resource(): with semaphore: print(f"Thread {threading.current_thread().name} accessing resource")
6.2 条件变量(Condition)
用于线程之间的通信和协调,常用于生产者-消费者模型:
condition = threading.Condition()queue = []def producer(): with condition: queue.append("item") condition.notify() # 通知等待的线程def consumer(): with condition: while not queue: condition.wait() # 等待队列非空 item = queue.pop(0)
6.3 事件(Event)
允许线程等待一个事件发生:
event = threading.Event()def worker(): print("Waiting for event...") event.wait() # 等待事件被设置 print("Event fired!")# 在主线程中触发事件event.set()
6.4 队列(Queue)
线程安全的队列,官方推荐优先使用:
import queueq = queue.Queue(maxsize=10)def producer(): q.put("item")def consumer(): item = q.get() # 自动处理锁与条件变量
优势:
七、GIL(全局解释器锁)的影响
7.1 GIL 的作用
Python 的全局解释器锁(GIL)确保同一时刻只有一个线程执行 Python 字节码。
7.2 GIL 的影响
- CPU 密集型任务: 多线程无法利用多核优势,性能提升有限甚至下降,建议使用
multiprocessing 模块 - I/O 密集型任务: 线程在等待 I/O 时会释放 GIL,多线程仍能有效提升并发性能
八、@dataclass 装饰器
8.1 基本概念
@dataclass 是 Python 3.7 引入的装饰器,位于标准库 dataclasses 模块中,用于自动为类生成常用的特殊方法。
8.2 自动生成的方法
- __init__(): 按字段顺序初始化
- __repr__(): 提供包含类名和所有字段值的清晰表示
- __eq__(): 基于字段值进行相等性比较
8.3 使用示例
from dataclasses import dataclass@dataclassclass Task: id: str subject: str description: str status: str # pending | in_progress | completed owner: str | None # Agent name blockedBy: list[str] # Dependency task IDs
8.4 高级用法
@dataclassclass Task: id: str status: str = "pending" def __post_init__(self): # 初始化后自动验证 if self.status not in ["pending", "in_progress", "completed"]: raise ValueError(f"Invalid status: {self.status}")
九、JSON 与字典解包
9.1 读取 JSON 文件
import jsonfrom pathlib import Pathdef load_task(task_id: str) -> Task: return Task(**json.loads(_task_path(task_id).read_text()))
9.2 ** 字典解包操作符
data = {"name": "Alice", "age": 30}# Person(**data) 等价于 Person(name="Alice", age=30)
十、常用字符串方法
| | | |
|---|
| 方法 | 功能 | 返回值 | 默认行为 |
strip() | | | |
split() | | | |
splitlines() | | | |
startswith() | | | |
总结
- 多线程编程的核心是正确处理共享资源的访问,避免竞态条件
- 锁(Lock) 是最基础的同步机制,推荐使用
with - 可重入锁(RLock) 解决同一线程多次加锁问题
- 高级同步机制(Semaphore、Condition、Event、Queue)适用于特定场景
- GIL 限制了 CPU 密集型任务的并行性能,但 I/O 密集型任务仍受益
- @dataclass 简化数据类的定义,自动生成样板代码
- 字典解包 方便从 JSON 等数据格式直接创建对象