问题:
- 现实场景:爬虫需要并发请求 100 个 URL / GUI 程序需避免界面卡顿Python 的两种并发方案对比:多线程 (threading) vs 多进程 (multiprocessing)抛出核心问题:为什么 Python 多线程有时比单线程更慢?
Python多线程编程基础:快速上手
- 创建线程的两种方式
- 直接传入目标函数继承Tread类
def worker(task_id): print(f"Processing task {task_id}")thread = Thread(target=worker, args=(1,))from threading import Threadclass MyThread(Thread): def run(self): print(f"Thread {self.name} is running")thread = MyThread()thread.start() # 启动线程而非直接调用run()
- 线程生命周期管理
- start() vs join()
threads = [Thread(target=worker, args=(i,)) for i in range(5)]for t in threads: t.start()for t in threads: t.join() # 等待所有线程完成
- 守护线程(daemon thread)的使用场景
t = Thread(target=background_task, daemon=True)t.start() # 主线程退出时自动终止
线程同步
当多个线程对共享资源进行操作时,可能会引发数据不一致的问题,这时就需要进行线程同步。Python提供了多种同步原语:
- Lock锁
import threadingcounter = 0lock = threading.Lock()def increment(): global counter for _ in range(100000): lock.acquire() # 获取锁 try: counter += 1 finally: lock.release() # 释放锁# 创建并启动线程threads = [threading.Thread(target=increment) for _ in range(2)]for t in threads: t.start()for t in threads: t.join()print(f'最终结果: {counter}') # 结果应为200000
- RLock可重入锁同一个线程可以多次获得RLock,适合在递归或者嵌套场景中使用Semaphore信号量用于控制同时访问资源的线程数量
semaphore = threading.Semaphore(3) # 最多允许3个线程同时访问def worker(): with semaphore: # 获取信号量 print('正在访问资源...')
- Condition条件变量用于线程间通信和协调
condition = threading.Condition()def consumer(): with condition: condition.wait() # 等待通知 print('收到通知,开始消费')def producer(): with condition: print('生产完成,发送通知') condition.notify() # 发送通知
线程间通信Queue vs 共享变量
线程间可以通过共享变量和队列Queue来交换数据,使用queue.Queue()是线程安全的方式
from queue import Queueimport threadingdef producer(queue): for item in range(5): queue.put(item) print(f'生产: {item}')def consumer(queue): while True: item = queue.get() print(f'消费: {item}') queue.task_done() # 通知队列任务已完成q = Queue()t1 = threading.Thread(target=producer, args=(q,))t2 = threading.Thread(target=consumer, args=(q,), daemon=True) # 守护线程t1.start()t2.start()t1.join() # 等待生产者线程完成q.join() # 等待队列中的所有任务被处理完