Python
基础语法
01概念
02安装
03变量
04字符串
05数
06常量与注释
07列表
08元组
09if语句
10字典
11集合
12复合数据类型对比
13推导式
14用户输入
15while循环
16函数
17类
18面向对象编程
19文件操作
20异常处理
21日期和时间
22魔术方法
23内置函数
24线程
25并发&并行
26正则表达式
27迭代器
28装饰器
29生成器
30上下文管理器
31函数式编程
32闭包
33解包
34工具库
35连接关系型数据库
36虚拟环境
37异步编程
网络爬虫
01urllib库[了解]
02requests库
03数据交换格式
04解析库
05lxml
06Beautiful Soup
07Xpath语法
08动态网页的处理
-
+
首页
24线程
线程是操作系统与编程语言中实现**并发执行**的核心机制,是**进程(Process)**的基本执行单元。它既依赖进程提供的资源(如内存空间、文件句柄),又能独立调度执行,是平衡 “并发效率” 与 “资源开销” 的关键技术。 ## 概述 线程(Thread)是**操作系统能够进行调度的最小执行单元**,隶属于某个进程。一个进程至少包含一个线程(称为 “主线程”),同时可创建多个 “子线程”,所有线程共享进程的核心资源(如虚拟地址空间、全局变量、打开的文件描述符),但拥有独立的**程序计数器(PC)、栈空间(Stack)、寄存器集合**。 线程与进程的区别: | 对比维度 | 进程(Process) | 线程(Thread) | | ---------------- | --------------------------------------------------------- | ---------------------------------- | | **资源分配单位** | 操作系统资源分配的基本单位(如内存、CPU 时间片的 “容器”) | 不独立分配资源,共享所属进程的资源 | | **调度执行单位** | 不直接参与调度,是线程的 “载体” | 操作系统调度执行的最小单位 | | **上下文切换** | 开销大(需切换内存映射、文件句柄等资源) | 开销小(仅需切换 PC、栈、寄存器) | | **独立性** | 进程间地址空间独立,通信需 IPC(如管道、Socket) | 线程间共享地址空间,通信更便捷 | | **稳定性** | 一个进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 | ## 特性 线程的设计初衷是 “在并发执行中降低资源开销”,其特性围绕 “共享” 与 “独立” 的平衡展开。 **1.轻量级:低开销的并发单元** 线程的 “轻量” 体现在**上下文切换(Context Switch)开销远低于进程**: - 进程切换需更新 “内存页表”“进程控制块(PCB)”“文件描述符表” 等重量级资源,耗时通常在**毫秒(ms)级**; - 线程切换仅需更新 “程序计数器(PC)”“栈指针”“寄存器值”,耗时通常在**微秒(μs)级**,是进程切换的 1/100~1/10。 **2.资源共享:高效通信的基础** 同一进程内的所有线程共享以下资源(无需额外 IPC 机制): - 全局变量、静态变量(存储在进程的 “数据段”); - 堆内存(动态分配的内存,如new/malloc创建的对象); - 打开的文件、网络连接、信号量等内核资源; - 进程的虚拟地址空间(代码段、数据段、堆段)。 **3.执行独立:并发调度的前提** 每个线程拥有独立的 “执行上下文”,确保调度时互不干扰: - **程序计数器(PC)**:记录当前线程执行到的指令地址; - **线程栈(Thread Stack)**:存储线程的局部变量、函数调用栈帧(每个函数调用会创建一个栈帧,线程退出后栈自动释放); - **寄存器集合**:CPU 执行时的临时数据存储(如通用寄存器、程序状态字 PSW),线程切换时需保存 / 恢复。 **4.并发与并行的统一** - **并发(Concurrency)**:多个线程在 “单个 CPU” 上通过 “时间片轮转” 交替执行,宏观上同时进行(如单核 CPU 上的多线程下载); - **并行(Parallelism)**:多个线程在 “多核 CPU” 上同时执行,微观上真正同步推进(如多核 CPU 上的线程池处理任务); 线程是实现这两种模式的统一载体 —— 操作系统通过调度线程,自动适配 “单核并发” 与 “多核并行”。 ## 优缺点 **优点**: - **高并发效率**:轻量级上下文切换,比进程更适合高并发场景; - **资源利用率高**:共享进程资源,减少内存、文件句柄等资源的消耗; - **多核适配**:支持多核 CPU 并行执行,充分利用硬件性能; - **开发便捷**:编程语言提供完善的线程 API(如Thread类、线程池),降低并发开发门槛。 **缺点:** - **线程安全风险**:共享资源易导致竞争条件,需额外同步机制,增加开发复杂度; - **调试难度大**:线程调度无序,难以复现并发 BUG(如死锁、竞态条件); - **资源限制**:过多线程会占用大量内存(栈空间),导致 OOM(内存溢出); - **GIL 限制**:部分语言(如 CPython)的线程在 CPU 密集型任务下效率受限。 ## 创建与启动 在 Python 中,线程操作主要通过标准库threading模块实现,其语法结构简洁且功能完善,涵盖线程创建、启动、同步、通信等核心场景。 threading.Thread是 Python 线程操作的核心类,用于创建线程对象,核心语法如下: ```python import threading # 创建线程对象 thread_obj = threading.Thread( target=函数名, # 线程执行的目标函数(必填) args=参数元组, # 传给目标函数的参数(可选,元组形式) kwargs=参数字典, # 传给目标函数的关键字参数(可选,字典形式) name=线程名 # 线程名称(可选,用于调试) ) # 启动线程(进入就绪状态,等待CPU调度) thread_obj.start() ``` 两种创建方式: **第一种函数式创建(推荐)**,直接将目标函数传递给Thread。 ```python import threading import time def print_numbers(name, count): for i in range(count): print(f"线程{name}:{i}") time.sleep(0.1) # 模拟耗时操作 # 创建线程(args传参,元组形式) t1 = threading.Thread(target=print_numbers, args=("T1", 3), name="Thread-1") t2 = threading.Thread(target=print_numbers, args=("T2", 2), name="Thread-2") # 启动线程 t1.start() t2.start() ``` 运行第一次,输出结果(顺序可能因调度变化): ```python 线程T1:0 线程T2:0 线程T1:1 线程T2:1 线程T1:2 ``` 运行第二次,输出结果: ```python 线程T1:0 线程T2:0 线程T2:1线程T1:1 线程T1:2 ``` 第二种方式:**类继承式创建**(适合复杂逻辑) 继承Thread类并重写run()方法(线程执行的核心逻辑): ```python import threading import time class MyThread(threading.Thread): def __init__(self, name, count): super().__init__(name=name) # 调用父类构造方法 self.count = count # 重写run()方法,线程启动后自动执行 def run(self): for i in range(self.count): print(f"线程{self.name}:{i}") time.sleep(0.1) # 创建并启动线程 t1 = MyThread("T1", 3) t2 = MyThread("T2", 2) t1.start() t2.start() ``` 运行第一次,输出结果: ```python 线程T1:0 线程T2:0 线程T1:1 线程T2:1 线程T1:2 ``` 运行第二次,输出结果: ```python 线程T1:0 线程T2:0 线程T2:1 线程T1:1 线程T1:2 ``` ## 线程周期 **1.start() vs run()** - start():启动线程,将线程放入就绪队列,由操作系统调度执行run()方法(**只能调用一次**,重复调用会抛RuntimeError)。 - run():线程的执行逻辑(函数式创建中对应target函数,类继承式中需重写),**直接调用run()会以普通函数方式执行,不会创建新线程**。 **2. join([timeout]):等待线程结束** 阻塞当前线程(通常是主线程),直到被调用的线程执行完毕或超时(timeout为秒数,可选),用于控制线程执行顺序。 ```python import threading import time def worker(): time.sleep(2) print("子线程执行完毕") t = threading.Thread(target=worker) t.start() # 主线程等待子线程结束(最多等3秒) t.join(3) # 若子线程2秒内完成,主线程立即继续;若超过3秒,主线程不再等待 print("主线程继续执行") ``` 输出结果为: ```python 子线程执行完毕 主线程继续执行 ``` t.join(2)时,再次运行后的输出结果为: ```python 子线程执行完毕 主线程继续执行 ``` t.join(1)时,再次运行后的输出结果为: ```python 主线程继续执行 子线程执行完毕 ``` **3. daemon属性:守护线程(后台线程)** - 守护线程(daemon=True):主线程结束时,无论守护线程是否执行完毕,都会被强制终止(如日志收集线程)。 - 非守护线程(默认daemon=False):主线程会等待所有非守护线程结束后才退出。 设置方式为: ```python t = threading.Thread(target=worker) t.daemon = True # 必须在start()前设置 t.start() ``` 例如:守护线程随主线程退出。 ```python import threading import time def daemon_worker(): while True: print("守护线程运行中...") time.sleep(1) t = threading.Thread(target=daemon_worker) t.daemon = True # 设置为守护线程 t.start() # 主线程休眠2秒后退出 time.sleep(2) print("主线程退出,守护线程被终止") ``` 输出结果: ```python 守护线程运行中... 守护线程运行中... 主线程退出,守护线程被终止 ``` ## 线程同步 线程同步:解决资源竞争(Lock、RLock)。 当多个线程共享资源(如全局变量)时,需通过**锁(Lock)** 保证操作的原子性,避免竞态条件。 **1. threading.Lock:互斥锁** 核心方法:acquire()(获取锁,若锁被占用则阻塞)、release()(释放锁)。 语法结构: ```python lock = threading.Lock() # 获取锁 lock.acquire() try: # 临界区:操作共享资源 shared_resource += 1 finally: # 确保锁释放,避免死锁 lock.release() ``` 简化写法(with语句自动管理锁的获取与释放): ```python with lock: # 临界区:自动获取锁,执行完后自动释放 shared_resource += 1 ``` 例如:用锁解决计数器竞态问题。 ```python import threading count = 0 lock = threading.Lock() # 创建锁 def increment(): global count for _ in range(100000): with lock: # 保证count++的原子性 count += 1 # 创建两个线程同时操作count t1 = threading.Thread(target=increment) t2 = threading.Thread(target=increment) t1.start() t2.start() t1.join() t2.join() print(f"最终count值:{count}") # 输出:200000(无锁时可能小于200000) ``` 输出结果: ```python 最终count值:200000 ``` **2. threading.RLock:可重入锁** 解决 “同一线程多次获取同一锁” 的死锁问题(如递归函数中需要重复加锁): ```python import threading rlock = threading.RLock() def recursive_func(n): with rlock: # 同一线程可多次获取锁 if n > 0: print(n) recursive_func(n-1) t = threading.Thread(target=recursive_func, args=(3,)) t.start() ``` 输出结果: ```python 3 2 1 ``` ## 线程通信 线程间需协作时(如 “生产者 - 消费者” 模型),可通过以下机制通信: **1. threading.Event:事件通知** - 核心方法:set()(设置事件为 “已触发”)、clear()(重置为 “未触发”)、wait([timeout])(等待事件触发,超时返回False)。 - 用于 “一个线程通知其他线程某个条件已满足”。 例如:主线程通知子线程启动。 ```python import threading import time event = threading.Event() # 初始为未触发状态 def worker(): print("子线程等待通知...") event.wait() # 阻塞等待事件触发 print("子线程收到通知,开始执行") t = threading.Thread(target=worker) t.start() # 主线程准备工作 time.sleep(2) print("主线程准备完毕,发送通知") event.set() # 触发事件 ``` 输出结果: ```python 子线程等待通知... 主线程准备完毕,发送通知 子线程收到通知,开始执行 ``` **2. threading.Condition:条件变量** 结合锁与事件的功能,支持 “等待 - 通知” 机制,适合复杂协作(如队列满 / 空时的等待)。 核心方法: - acquire()/release():获取 / 释放底层锁(同Lock)。 - wait():释放锁并阻塞等待,被通知后重新获取锁。 - notify(n=1):唤醒 n 个等待的线程。 - notify_all():唤醒所有等待的线程。 例如:生产者 - 消费者模型 ```python import threading import time from collections import deque queue = deque(maxlen=5) # 缓冲区(最多存5个元素) condition = threading.Condition() # 条件变量 # 生产者:生成数据 def producer(): for i in range(10): with condition: # 若缓冲区满,等待消费者取走数据 while len(queue) == 5: condition.wait() queue.append(i) print(f"生产者放入:{i},当前缓冲区:{list(queue)}") condition.notify() # 通知消费者 time.sleep(0.1) # 消费者:取走数据 def consumer(): for _ in range(10): with condition: # 若缓冲区空,等待生产者放入数据 while len(queue) == 0: condition.wait() data = queue.popleft() print(f"消费者取出:{data},当前缓冲区:{list(queue)}") condition.notify() # 通知生产者 time.sleep(0.2) # 启动线程 t1 = threading.Thread(target=producer) t2 = threading.Thread(target=consumer) t1.start() t2.start() ``` 输出结果: ```python 生产者放入:0,当前缓冲区:[0] 消费者取出:0,当前缓冲区:[] 生产者放入:1,当前缓冲区:[1] 生产者放入:2,当前缓冲区:[1, 2] 消费者取出:1,当前缓冲区:[2] 生产者放入:3,当前缓冲区:[2, 3] 消费者取出:2,当前缓冲区:[3] 生产者放入:4,当前缓冲区:[3, 4] 生产者放入:5,当前缓冲区:[3, 4, 5] 消费者取出:3,当前缓冲区:[4, 5] 生产者放入:6,当前缓冲区:[4, 5, 6] 生产者放入:7,当前缓冲区:[4, 5, 6, 7] 消费者取出:4,当前缓冲区:[5, 6, 7] 生产者放入:8,当前缓冲区:[5, 6, 7, 8] 生产者放入:9,当前缓冲区:[5, 6, 7, 8, 9] 消费者取出:5,当前缓冲区:[6, 7, 8, 9] 消费者取出:6,当前缓冲区:[7, 8, 9] 消费者取出:7,当前缓冲区:[8, 9] 消费者取出:8,当前缓冲区:[9] 消费者取出:9,当前缓冲区:[] ``` **3. queue.Queue:线程安全队列(推荐)** queue模块提供的Queue是线程安全的阻塞队列,内置同步机制,无需手动加锁,是生产者 - 消费者模型的最佳实践。 核心方法: - put(item, block=True, timeout=None):放入元素(队列满时阻塞)。 - get(block=True, timeout=None):取出元素(队列空时阻塞)。 - task_done():通知队列任务已处理完毕。 - join():等待所有元素被处理完毕。 例如:用Queue实现生产者 - 消费者。 ```python import threading import time from queue import Queue q = Queue(maxsize=5) # 最大容量5 def producer(): for i in range(10): q.put(i) # 队列满时自动阻塞 print(f"生产者放入:{i}") time.sleep(0.1) def consumer(): for _ in range(10): data = q.get() # 队列空时自动阻塞 print(f"消费者取出:{data}") q.task_done() # 标记任务完成 time.sleep(0.2) t1 = threading.Thread(target=producer) t2 = threading.Thread(target=consumer) t1.start() t2.start() q.join() # 等待所有元素被处理 print("所有任务完成") ``` 输出结果: ```python 生产者放入:0 消费者取出:0 所有任务完成 生产者放入:1 生产者放入:2消费者取出:1 生产者放入:3 消费者取出:2 生产者放入:4 生产者放入:5 消费者取出:3 生产者放入:6 生产者放入:7 消费者取出:4 生产者放入:8 生产者放入:9 消费者取出:5 消费者取出:6 消费者取出:7 消费者取出:8 消费者取出:9 ``` ## 相关函数 threading模块提供了实用工具函数,用于线程管理和调试: | 函数 | 功能描述 | | -------------------------- | -------------------------------------------- | | threading.current_thread() | 返回当前正在执行的线程对象 | | threading.enumerate() | 返回所有存活的线程列表(包括主线程) | | threading.active_count() | 返回存活线程的数量(等价于len(enumerate())) | | threading.main_thread() | 返回主线程对象 | 例如:查看线程信息。 ```python import threading import time def worker(): time.sleep(1) t = threading.Thread(target=worker, name="Worker-1") t.start() print("当前线程:", threading.current_thread().name) # 输出:MainThread print("所有线程:", [t.name for t in threading.enumerate()]) # 输出:['MainThread', 'Worker-1'] print("活跃线程数:", threading.active_count()) # 输出:2 ``` 输出结果: ```python 当前线程: MainThread 所有线程: ['MainThread', 'Worker-1'] 活跃线程数: 2 进程已结束,退出代码为 0 ``` ## 核心状态 Python 线程的生命周期可划分为 **5 个核心状态**,每个状态对应明确的创建条件、行为特征和底层逻辑。所有状态的转换均围绕Thread对象的方法(如start()、join())和外部事件(如 IO 完成、锁释放)触发。 **1. 新建状态(New)** **定义**:创建threading.Thread对象,但未调用start()方法时的状态。此时仅在 Python 解释器层面存在一个线程对象,**未关联操作系统的实际线程**(OS 线程),也不占用 CPU 资源。 **特征**: - Thread.is_alive()返回False(线程未激活); - 无法通过threading.enumerate()获取(仅活跃线程会被枚举); - 仅能通过start()方法进入下一状态,直接调用run()不会触发状态转换(仅普通函数调用)。 ```python import threading def task(): print("子线程任务") # 新建状态:仅创建Thread对象,未start() t = threading.Thread(target=task, name="MyThread") print(f"线程是否存活:{t.is_alive()}") # 输出:False(新建状态) print(f"当前活跃线程:{threading.enumerate()}") # 仅包含主线程 ``` 输出结果: ```python 线程是否存活:False 当前活跃线程:[<_MainThread(MainThread, started 7804)>] ``` **2. 就绪状态(Runnable)** **定义**:调用Thread.start()方法后,Python 解释器会向操作系统申请创建一个**OS 线程**,并将该线程加入「CPU 就绪队列」。此时线程已具备运行条件,但需等待**两个资源**: 1. 操作系统的 CPU 时间片(调度); 2. Python 的全局解释器锁(GIL)—— 同一进程内仅一个线程可持有 GIL。 **特征**: - Thread.is_alive()返回True(线程已激活); - 可通过threading.enumerate()枚举到(属于活跃线程); - 线程未执行task逻辑,仅等待调度。 ```python t.start() # 调用start(),进入就绪状态 print(f"线程是否存活:{t.is_alive()}") # 输出:True(就绪/运行状态) print(f"当前活跃线程:{threading.enumerate()}") # 包含主线程+子线程 ``` 输出结果: ```python 子线程任务线程是否存活:True 当前活跃线程:[<_MainThread(MainThread, started 16264)>, <Thread(MyThread, started 53044)>] ``` **3. 运行状态(Running)** **定义**:就绪状态的线程成功获取「CPU 时间片」和「GIL」后,开始执行target参数指定的任务(如task函数),即进入运行状态。 **特征**: - 同一进程内**同时仅有一个线程**能处于运行状态(受 GIL 限制); - 线程主动执行业务逻辑,直到发生以下事件之一才退出运行状态: - 时间片耗尽(OS 调度,释放 CPU 和 GIL); - 遇到 IO 操作(如time.sleep()、网络请求),主动释放 GIL; - 请求未获取的锁(如Lock.acquire()); - 任务执行完毕或抛出未捕获异常。 ```python import threading import time def task(): print("子线程任务") def task(): print("子线程进入运行状态") # 运行状态的核心逻辑 time.sleep(1) # 触发阻塞,暂时退出运行状态 t = threading.Thread(target=task) t.start() # 就绪→运行(获取CPU+GIL后) print(f"线程是否存活:{t.is_alive()}") # 输出:True(就绪/运行状态) print(f"当前活跃线程:{threading.enumerate()}") # 包含主线程+子线程 ``` 输出结果: ```python 子线程进入运行状态线程是否存活:True 当前活跃线程:[<_MainThread(MainThread, started 16780)>, <Thread(Thread-1 (task), started 42216)>] ``` **4. 阻塞状态(Blocked)** **定义**:运行中的线程因等待「特定事件」(如 IO 完成、锁释放),暂时放弃 CPU 和 GIL,进入阻塞状态。此时线程不再参与 CPU 调度,直到事件触发后重新回到就绪状态。 **常见阻塞场景**(均会主动释放 GIL,让其他线程运行): | 阻塞类型 | 触发操作 | 唤醒条件 | | ------------ | --------------------------------------- | -------------------------------------- | | IO 阻塞 | time.sleep(n)、requests.get()、文件读写 | IO 操作完成(如 sleep 超时、请求响应) | | 锁阻塞 | threading.Lock.acquire()(锁已被占用) | 其他线程释放锁 | | join 阻塞 | 主线程调用子线程.join() | 子线程执行完毕(进入终止状态) | | 条件变量阻塞 | threading.Condition.wait() | 其他线程调用notify()/notify_all() | **特征**: - Thread.is_alive()仍为True(线程未终止); - 不占用 CPU 资源,但 OS 线程仍存在; - 仅能通过「唤醒条件」回到就绪状态,无法直接进入运行或终止状态。 ```python import threading import time lock = threading.Lock() def task(lock): print("子线程尝试获取锁...") lock.acquire() # 若锁已被占用,进入阻塞状态 try: print("子线程获取锁,进入运行状态") time.sleep(2) finally: lock.release() # 主线程先获取锁,导致子线程阻塞 lock.acquire() t = threading.Thread(target=task, args=(lock,)) t.start() time.sleep(1) # 主线程休眠1秒,观察子线程状态 print(f"子线程是否存活(阻塞中):{t.is_alive()}") # 输出:True(阻塞状态) lock.release() # 主线程释放锁,子线程从阻塞→就绪 t.join() ``` 输出结果: ```python 子线程尝试获取锁... 子线程是否存活(阻塞中):True 子线程获取锁,进入运行状态 ``` **5. 终止状态(Terminated)** **定义**:线程的任务逻辑执行完毕,或因未捕获异常导致任务中断,此时 OS 线程被销毁,Python 的Thread对象进入终止状态。 **触发条件**: 1. 正常终止:target函数(如task)执行完毕; 2. 异常终止:target函数抛出未捕获的异常(如ValueError、IndexError)。 **特征**: - Thread.is_alive()返回False(线程已终止); - OS 线程被销毁,不再占用任何资源; - 无法再转换到其他状态(如调用start()会抛出RuntimeError)。 ```python import threading def task(): print("子线程运行中...") raise ValueError("任务执行出错") # 未捕获异常,导致线程终止 t = threading.Thread(target=task) t.start() t.join() print(f"子线程是否存活(异常后):{t.is_alive()}") # 输出:False(终止状态) ``` 输出结果: ```python 子线程运行中... 子线程是否存活(异常后):False ``` ## 状态转换流程 Python 线程的生命周期是一个**有序的状态转换过程**,不存在 “跳跃式转换”(如阻塞→终止、新建→运行)。 转换流程如下: ```txt 新建状态(New) ↓(调用Thread.start()) 就绪状态(Runnable) ↓(获取CPU+GIL) 运行状态(Running) ├─→ ↓(时间片耗尽/被抢占) │ 就绪状态(Runnable) ├─→ ↓(IO/锁/join等阻塞事件) │ 阻塞状态(Blocked) │ ↓(阻塞事件唤醒) │ 就绪状态(Runnable) └─→ ↓(任务完成/未捕获异常) 终止状态(Terminated) ``` **关键转换规则总结** 1. **新建→就绪**:唯一触发方式是Thread.start(),不可跳过(直接调用run()无效); 2. **就绪→运行**:依赖 OS 调度(CPU 时间片)和 GIL 获取,不可人工干预; 3. **运行→阻塞**:线程主动放弃资源(如 IO 等待),是多线程效率提升的关键(释放 GIL 让其他线程运行); 4. **阻塞→就绪**:必须等待 “唤醒条件”(如 IO 完成、锁释放),唤醒后需重新等待 CPU 和 GIL; 5. **运行→终止**:任务正常完成或异常中断,是线程生命周期的终点。 ## 注意事项 理解 Python 线程生命周期,必须结合其特有的设计限制(如 GIL)和最佳实践。 ### GIL 对线程状态的影响 **核心规则**:Python 的 GIL 确保同一进程内**同时仅有一个线程**能处于「运行状态」,即使在多核 CPU 上也无法实现 “真正并行”。 **对生命周期的影响**: - 就绪状态的线程实际是 “等待 GIL” 而非 “等待 CPU”(CPU 通常空闲); - 线程进入阻塞状态(如sleep)时会**主动释放 GIL**,让其他就绪线程进入运行状态(这也是 Python 多线程在 IO 密集型任务中有效的原因); - CPU 密集型任务中,多线程因 GIL 频繁切换,效率可能低于多进程(multiprocessing模块)。 ### start()与run()的本质区别 - **start()**:触发 OS 线程创建,将线程从「新建」→「就绪」,是线程激活的唯一合法方式; - **run()**:仅为Thread对象的普通方法,直接调用会在**主线程中执行task逻辑**,不会创建新线程,线程始终处于「新建」状态(is_alive()始终为False)。 ### 线程的优雅退出(避免强制终止) - Python 的threading模块**不提供安全的强制终止方法**(如 Java 的stop()),强制终止可能导致资源泄漏(如锁未释放、文件未关闭)。 - **最佳实践**:使用「标志位」让线程主动退出(如前文示例中的stop_flag),或通过queue.Queue传递终止信号。 ### 异常处理(避免线程意外终止) - 子线程中未捕获的异常会直接导致线程终止,且**不会通知主线程**(主线程仍正常运行)。 - **解决方案**:在target函数内部添加try-except块,捕获异常并处理(如日志记录): ```python import threading def task(): try: # 业务逻辑 1 / 0 # 触发ZeroDivisionError except Exception as e: print(f"子线程捕获异常:{e}") # 日志记录 # 可选:释放资源(如锁、文件) t = threading.Thread(target=task) t.start() t.join() ``` 输出结果: ```python 子线程捕获异常:division by zero ```
毛林
2025年9月7日 11:45
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码