Python3 多线程

一、核心基础

1. 线程核心概念

概念 定义 / 说明
线程 进程内最小执行单元(轻量级进程),共享进程资源,拥有独立执行栈和程序计数器
核心特点 共享资源、独立执行流、轻量级(创建 / 销毁开销小)、可抢占 / 协作
核心优势 提升 IO 密集型任务效率、后台处理耗时任务、优化 GUI 交互、简化通信(共享内存)
线程分类 内核线程(OS 创建)、用户线程(用户程序实现)

2. 线程 vs 进程

对比维度 线程 进程
资源分配 共享所属进程资源 独立分配资源(内存、CPU)
开销 小(仅初始化执行栈) 大(分配内存、加载程序)
通信方式 直接读写共享变量(需同步) 依赖 IPC(管道、套接字等)
独立性 低(线程崩溃可能导致进程崩溃) 高(进程崩溃不影响其他进程)
调度单位 操作系统最小调度单元 操作系统最小资源分配单元

3. 核心模块

模块 定位 功能特点 推荐程度
_thread 低级原始模块 基础线程创建,仅兼容保留,功能有限(无完善管理方法) ❌ 不推荐
threading 高级完善模块 包含_thread所有功能,支持线程管理、同步、辅助函数,功能全面 ✅ 推荐

二、线程创建与启动(2 种核心方式)

1. 函数式创建(简单场景)

核心模板
复制代码
import threading
import time

# 1. 定义线程执行函数
def task(name, count):
    for i in range(count):
        print(f"线程{name}:{i}")
        time.sleep(0.5)

# 2. 创建线程(target=函数,args=参数元组)
t1 = threading.Thread(target=task, args=("A", 5))
t2 = threading.Thread(target=task, args=("B", 3))

# 3. 启动线程(必须用start(),不可直接调用函数)
t1.start()
t2.start()

# 4. 等待线程结束(主线程阻塞)
t1.join()
t2.join()
print("所有线程完成")
关键要点
  • args 必须是元组类型,单个参数需加逗号(如(1,)
  • 启动线程用 start(),底层自动调用 run() 方法

2. 类继承式创建(复杂场景)

核心模板
复制代码
import threading
import time

# 1. 自定义线程类,继承threading.Thread
class MyThread(threading.Thread):
    # 2. 重写__init__,初始化线程属性
    def __init__(self, name, count):
        super().__init__()  # 必须调用父类构造
        self.name = name
        self.count = count

    # 3. 重写run(),定义线程执行逻辑
    def run(self):
        for i in range(self.count):
            print(f"线程{self.name}:{i}")
            time.sleep(0.5)

# 4. 创建并启动线程
t1 = MyThread("A", 5)
t2 = MyThread("B", 3)
t1.start()
t2.start()

# 5. 等待线程结束
t1.join()
t2.join()
print("所有线程完成")
关键要点
  • 必须重写 run() 方法(线程核心逻辑)
  • 无需手动调用 run()start() 会自动触发

三、线程管理核心能力

1. 核心方法与属性

方法 / 属性 功能描述 关键注意事项
start() 启动线程,触发run() 不可重复调用,否则报错
join(timeout) 主线程等待该线程终止,timeout为超时时间(秒) 无超时则一直阻塞
is_alive() 判断线程是否运行(启动后、终止前返回True 常用于判断线程状态
daemon 守护线程标志(True:主线程退出则自动终止) 必须在start()前设置
getName()/setName() 获取 / 设置线程名称 便于调试多线程程序
ident 线程唯一标识符 只读属性,线程启动后生效

2. 辅助函数

函数名 功能描述
threading.current_thread() 返回当前正在执行的线程对象
threading.enumerate() 返回运行中的线程列表(不含未启动、已终止线程)
threading.active_count() 返回运行中线程数量,等同于len(threading.enumerate())

3. 重点:守护线程(daemon)

核心示例
复制代码
import threading
import time

def daemon_task():
    while True:
        print("守护线程运行中...")
        time.sleep(1)

# 设置为守护线程(必须在start()前)
t = threading.Thread(target=daemon_task, daemon=True)
t.start()

# 主线程运行3秒后退出,守护线程随之终止
time.sleep(3)
print("主线程退出")
应用场景
  • 日志收集、监控、后台清理等辅助任务
  • 无需独立于主线程存在的任务

四、线程同步与安全

1. 核心问题:线程不安全

  • 原因:多线程共享数据时,非原子操作(如num += 1)会导致数据错乱
  • 示例:两个线程同时修改共享变量,最终结果小于预期

2. 解决方案 1:Lock/Rlock 锁

核心模板(Lock)
复制代码
import threading

num = 0
lock = threading.Lock()  # 创建锁对象

def add_num():
    global num
    for _ in range(100000):
        lock.acquire()  # 获取锁(阻塞直到锁释放)
        num += 1        # 共享数据操作
        lock.release()  # 释放锁(必须执行,避免死锁)

t1 = threading.Thread(target=add_num)
t2 = threading.Thread(target=add_num)
t1.start()
t2.start()
t1.join()
t2.join()
print(f"正确结果:{num}")  # 200000
Lock vs Rlock
类型 特点 适用场景
Lock 非可重入锁,同一线程多次acquire()会死锁 简单互斥场景(无嵌套加锁)
Rlock 可重入锁,同一线程可多次acquire(),需对应release() 嵌套加锁场景(如函数调用函数)

3. 解决方案 2:Queue 模块(线程安全队列)

核心优势
  • 内置锁原语,无需手动加锁
  • 支持线程间安全的数据传递与任务调度
三种队列类型
队列类型 特点 适用场景
queue.Queue 先进先出(FIFO) 按顺序处理任务(订单、消息)
queue.LifoQueue 后进先出(LIFO) 优先处理最新任务(撤销操作)
queue.PriorityQueue 优先级队列((优先级, 数据) 紧急任务优先(告警、优先级任务)
核心模板(FIFO 队列)

运行并比对两段代码的运行结果有什么不同:

q.join() 不会等待生产者未来放入的任务,它只对调用时刻已在队列中的任务计数。当第一个任务被消费后,计数器归零,q.join() 立即返回。

python 复制代码
import queue
import threading
import time

# 创建队列(maxsize=5,满时阻塞)
q = queue.Queue(maxsize=5)

# 生产者:放数据
def producer():
    for i in range(10):
        q.put(f"数据{i}")
        print(f"生产者放入:数据{i}")
        time.sleep(0.5)

# 消费者:取数据
def consumer():
    while True:
        data = q.get()  # 空时阻塞
        print(f"消费者取出:{data}")
        q.task_done()  # 标记任务完成
        time.sleep(1)

# 启动线程
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer, daemon=True)
t1.start()
t2.start()

q.join()  # 等待队列所有任务完成
print("所有任务处理完毕")
python 复制代码
import queue
import threading
import time

q = queue.Queue(maxsize=5)

def producer():
    for i in range(10):
        q.put(f"数据{i}")
        print(f"生产者放入:数据{i}")
        time.sleep(0.5)
    print("生产者结束")  # 用于验证生产者是否完成

def consumer():
    while True:
        data = q.get()
        if data is None:  # 退出信号
            q.task_done()
            break
        print(f"消费者取出:{data}")
        q.task_done()
        time.sleep(1)

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer, daemon=True)

t1.start()
t2.start()

t1.join()       # ✅ 先等待生产者结束(确保10个数据全部入队)
q.join()        # ✅ 再等待队列清空
print("所有任务处理完毕")
核心方法
方法名 功能描述
put(item) 入队,队列满时阻塞
get() 出队,队列空时阻塞
empty()/full() 判断队列空 / 满
task_done() 标记任务完成,配合join()使用
join() 阻塞主线程,直到队列所有任务都被task_done()标记

五、关键坑点与解决方案

1. GIL 锁的影响

  • 什么是 GIL:CPython 的全局解释器锁,同一时间仅允许一个线程执行 Python 字节码
  • 核心影响:
    • IO 密集型任务:多线程效率提升明显(线程等待 IO 时释放 GIL)
    • CPU 密集型任务:多线程无优势(甚至因切换开销下降)
  • 解决方案:CPU 密集型用multiprocessing(多进程)

2. 死锁问题

  • 原因:多个线程互相等待对方释放锁(如线程 A 持有锁 1,等待锁 2;线程 B 持有锁 2,等待锁 1)
  • 避免方法:
    • 统一锁的获取顺序
    • 使用try-finally确保锁释放
    • Rlock替代Lock(适用于嵌套场景)

3. 线程启动误区

  • 错误:直接调用run()方法(等同于主线程同步执行,非多线程)
  • 正确:必须调用start()方法(触发线程异步执行)

六、实战总结与口诀

1. 核心口诀

  • 线程创建:函数式(简单)、类继承(复杂),启动必用start()
  • 线程管理:join()等待、daemon守护、is_alive()判状态
  • 线程安全:共享数据用Lock,任务调度用Queue
  • 场景选择:IO 密集用线程,CPU 密集用进程

2. 常见场景技术选型

场景类型 推荐技术 原因
文件读写、网络请求 threading(多线程) IO 等待时释放 CPU,提升并发效率
数据计算、逻辑处理 multiprocessing(多进程) 绕过 GIL 锁,充分利用多核 CPU
任务有序调度 queue.Queue(FIFO 队列) 线程安全,按顺序执行任务
紧急任务优先 queue.PriorityQueue(优先级队列) 按优先级分配资源,高优先级任务先执行

3. 复习重点

  • 线程创建的两种方式及核心代码
  • 线程同步的两种方案(Lock/Queue)
  • GIL 锁的影响及场景选择
  • 守护线程的作用与设置
相关推荐
子午1 小时前
【食物识别系统】Python+TensorFlow+Vue3+Django+人工智能+深度学习+卷积网络+resnet50算法
人工智能·python·深度学习
曾经的三心草1 小时前
基于正倒排索引的Java文档搜索引擎2-实现Index类
java·python·搜索引擎
疏狂难除1 小时前
尝试rust与python的混合编程(二)
数据库·python·rust
n***33351 小时前
linux redis简单操作
linux·运维·redis
h***59331 小时前
使用Canal将MySQL数据同步到ES(Linux)
linux·mysql·elasticsearch
xu_yule2 小时前
网络和Linux网络-5(应用层)HTTP协议(方法+报头+状态码)
linux·网络·网络协议·http
n***4432 小时前
Node.js HTTP模块详解:创建服务器、响应请求与客户端请求
服务器·http·node.js
lhyzws2 小时前
CENTOS上的网络安全工具(三十二) Portainer Kafka-Clickhouse部署(1)
linux·kafka·centos
子午2 小时前
【蘑菇识别系统】Python+TensorFlow+Vue3+Django+人工智能+深度学习+卷积网络+resnet50算法
人工智能·python·深度学习