Python多线程全面解析,从使用到GIL的底层逻辑

问题:

  • 现实场景:爬虫需要并发请求 100 个 URL / GUI 程序需避免界面卡顿
  • Python 的两种并发方案对比:多线程 (threading) vs 多进程 (multiprocessing)
  • 抛出核心问题:为什么 Python 多线程有时比单线程更慢?

Python多线程编程基础:快速上手

  • 创建线程的两种方式
    • 直接传入目标函数
    • 继承Tread类
python 复制代码
def worker(task_id):
    print(f"Processing task {task_id}")

thread = Thread(target=worker, args=(1,))


from threading import Thread

class MyThread(Thread):
    def run(self):
        print(f"Thread {self.name} is running")

thread = MyThread()
thread.start()  # 启动线程而非直接调用run()
  • 线程生命周期管理
    • start() vs join()
css 复制代码
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)的使用场景
ini 复制代码
t = Thread(target=background_task, daemon=True)
t.start()  # 主线程退出时自动终止

线程同步

当多个线程对共享资源进行操作时,可能会引发数据不一致的问题,这时就需要进行线程同步。Python提供了多种同步原语:

  • Lock锁
csharp 复制代码
import threading

counter = 0
lock = 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信号量 用于控制同时访问资源的线程数量
python 复制代码
semaphore = threading.Semaphore(3)  # 最多允许3个线程同时访问
def worker():
    with semaphore:  # 获取信号量
        print('正在访问资源...')
  • Condition条件变量 用于线程间通信和协调
python 复制代码
condition = threading.Condition()

def consumer():
    with condition:
        condition.wait()  # 等待通知
        print('收到通知,开始消费')

def producer():
    with condition:
        print('生产完成,发送通知')
        condition.notify()  # 发送通知

线程间通信Queue vs 共享变量

线程间可以通过共享变量和队列Queue来交换数据,使用queue.Queue()是线程安全的方式

python 复制代码
from queue import Queue
import threading

def 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()   # 等待队列中的所有任务被处理完
相关推荐
zyplayer-doc3 小时前
VuePress类静态文档站和动态知识库怎么选:两种技术路线的适用场景
javascript·人工智能·后端·安全·智能手机
用户938515635075 小时前
从前后端分离到前端接口工程:React + MockJS + Vite 解析
前端·后端·全栈
码事漫谈6 小时前
世界由什么构成——这个问题比你想的更缠人
后端
摇滚侠6 小时前
《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 1
spring boot·笔记·后端
山荷枝6 小时前
03-框架--Spring
java·后端·spring
ChaHae-In7 小时前
SpringBoot统一功能处理
java·spring boot·后端
65岁退休Coder9 小时前
LangChain v1.3.4 笔记 - 07 补充:链式调用 LCEL
后端·python·langchain
卷无止境9 小时前
FastAPI 部署在 Nginx 后面到底该怎么配
后端·python