当前位置: 首页 > news >正文

Python线程全面详解:从基础概念到高级应用

一、线程基础概念

1.1 进程与线程的关系

进程是操作系统资源分配的基本单位,它是程序的一次执行过程。当我们将程序加载到内存中运行时,系统会为它分配CPU、内存、文件句柄等资源,这时就形成了一个进程。

线程是CPU调度的基本单位,它是进程中的一个执行流。一个进程可以包含多个线程,这些线程共享进程的资源,但每个线程有自己的执行路径和栈空间。

关键区别

  • 进程间相互独立,线程间共享进程资源

  • 进程切换开销大,线程切换开销小

  • 进程通信需要IPC机制,线程可直接读写进程数据段

1.2 为什么需要线程

虽然进程已经实现了多道编程,但仍存在两个主要缺陷:

  1. 单任务限制:传统进程一次只能执行一个任务

  2. 阻塞问题:进程某部分阻塞会导致整个进程挂起

现实类比:将上课看作一个进程,我们需要同时:

  • 耳朵听老师讲课(线程1)

  • 手上记笔记(线程2)

  • 脑子思考问题(线程3)

如果没有线程机制,这三件事只能顺序执行,效率低下。

二、Python线程实现

2.1 线程模块选择

Python提供了多个线程相关模块:

  • thread:基础线程模块(已过时,不推荐)

  • threading:高级线程接口(推荐使用)

  • Queue:线程安全队列实现

2.2 创建线程的两种方式

方式一:使用Thread类直接创建
from threading import Thread
import timedef task(num):time.sleep(0.5)print(f"线程{num}执行")if __name__ == '__main__':threads = []for i in range(3):t = Thread(target=task, args=(i,))threads.append(t)t.start()for t in threads:t.join()  # 等待所有线程完成
方式二:继承Thread类创建
from threading import Threadclass MyThread(Thread):def __init__(self, num):super().__init__()self.num = numdef run(self):print(f"自定义线程{self.num}执行")if __name__ == '__main__':t = MyThread(1)t.start()t.join()

2.3 Thread类常用方法

方法名描述
start()启动线程
join([timeout])等待线程终止
is_alive()返回线程是否存活
name线程名称
ident线程标识符
daemon是否为守护线程

2.4 threading模块常用函数

函数描述
threading.current_thread()返回当前线程对象
threading.active_count()当前活跃线程数
threading.enumerate()返回所有活跃线程列表
threading.main_thread()返回主线程对象

三、线程与进程性能对比

3.1 创建开销对比

from threading import Thread
from multiprocessing import Process
import timedef task(num):pass# 进程测试
start = time.time()
processes = []
for i in range(100):p = Process(target=task, args=(i,))p.start()processes.append(p)[p.join() for p in processes]
print("多进程耗时:", time.time()-start)  # 约11秒# 线程测试
start = time.time()
threads = []
for i in range(100):t = Thread(target=task, args=(i,))t.start()threads.append(t)[t.join() for t in threads]
print("多线程耗时:", time.time()-start)  # 约0.02秒

3.2 PID对比

import os
from threading import Thread
from multiprocessing import Processdef show_pid(label):print(f"{label} PID:", os.getpid())# 进程
p = Process(target=show_pid, args=("进程",))
p.start()  # 显示不同PID# 线程
t = Thread(target=show_pid, args=("线程",))
t.start()  # 显示相同PID

四、守护线程详解

4.1 守护线程 vs 守护进程

守护进程特点

  1. 随主进程结束而立即结束

  2. 主进程会等待非守护子进程完成

  3. 守护进程主要用于服务主进程

守护线程特点

  1. 随主线程结束而结束(实际是进程内所有非守护线程结束后)

  2. 主线程会等待所有非守护线程完成

  3. 守护线程通常用于后台支持任务

4.2 代码示例

from threading import Thread
import timedef daemon_task():print("守护线程开始")time.sleep(5)print("守护线程结束")  # 可能不会执行def normal_task():print("普通线程开始")time.sleep(2)print("普通线程结束")if __name__ == '__main__':d = Thread(target=daemon_task)d.daemon = Truen = Thread(target=normal_task)d.start()n.start()print("主线程结束")# 程序会在普通线程结束后退出,守护线程可能被强制结束

五、线程同步机制

5.1 互斥锁(Lock)

from threading import Thread, Lockcounter = 0
lock = Lock()def increment():global counterfor _ in range(100000):lock.acquire()counter += 1lock.release()threads = []
for i in range(5):t = Thread(target=increment)threads.append(t)t.start()for t in threads:t.join()print("Final counter:", counter)  # 正确结果500000

5.2 死锁问题与解决方案

死锁示例

from threading import Thread, Locklock1 = Lock()
lock2 = Lock()def func1():lock1.acquire()print("Func1获取lock1")lock2.acquire()print("Func1获取lock2")lock2.release()lock1.release()def func2():lock2.acquire()print("Func2获取lock2")lock1.acquire()print("Func2获取lock1")lock1.release()lock2.release()t1 = Thread(target=func1)
t2 = Thread(target=func2)
t1.start()
t2.start()
t1.join()
t2.join()

解决方案:使用RLock(可重入锁)

from threading import RLocklock = RLock()def recursive_func(n):if n > 0:lock.acquire()print(f"获取锁,n={n}")recursive_func(n-1)lock.release()recursive_func(3)

5.3 信号量(Semaphore)

from threading import Semaphore, Thread
import timesem = Semaphore(3)  # 允许最多3个线程同时访问def task(name):print(f"{name} 等待获取信号量")sem.acquire()print(f"{name} 获取了信号量")time.sleep(2)sem.release()print(f"{name} 释放了信号量")for i in range(10):t = Thread(target=task, args=(f"Thread-{i}",))t.start()

5.4 事件(Event)

from threading import Event, Thread
import timeevent = Event()def waiter():print("等待事件触发")event.wait()  # 阻塞直到事件被设置print("事件已触发,继续执行")def setter():time.sleep(3)print("设置事件")event.set()  # 触发事件Thread(target=waiter).start()
Thread(target=setter).start()

5.5 条件变量(Condition)

from threading import Condition, Threadcondition = Condition()
items = []def consumer():condition.acquire()if not items:print("消费者等待...")condition.wait()  # 释放锁并等待print(f"消费物品: {items.pop()}")condition.release()def producer():condition.acquire()items.append("新产品")print("生产者通知...")condition.notify()  # 唤醒一个等待线程condition.release()Thread(target=consumer).start()
Thread(target=producer).start()

六、线程池与高级用法

6.1 使用ThreadPoolExecutor

from concurrent.futures import ThreadPoolExecutor
import timedef task(name):print(f"任务 {name} 开始")time.sleep(2)return f"任务 {name} 完成"with ThreadPoolExecutor(max_workers=3) as executor:futures = [executor.submit(task, i) for i in range(5)]for future in futures:print(future.result())  # 获取任务结果

6.2 线程局部数据

from threading import Thread, localthread_data = local()
thread_data.x = 0def task():thread_data.x = 1print(f"子线程: {thread_data.x}")t = Thread(target=task)
t.start()
t.join()print(f"主线程: {thread_data.x}")  # 仍然是0,各线程独立

七、线程编程最佳实践

  1. 避免全局变量:尽量使用参数传递数据

  2. 合理使用锁:锁的范围要尽可能小

  3. 防止死锁:按固定顺序获取多个锁

  4. 优先使用队列:Queue是线程安全的通信方式

  5. 考虑GIL影响:CPU密集型任务考虑多进程

  6. 资源清理:确保线程结束时释放资源

  7. 异常处理:线程内异常不会传播到主线程

八、常见问题解答

Q:Python多线程真的能提高性能吗?
A:对于I/O密集型任务,多线程能显著提高性能;对于CPU密集型任务,由于GIL的存在,多线程可能不会提高性能,此时应考虑多进程。

Q:如何选择多线程还是多进程?
A:根据任务类型选择:

  • I/O密集型:多线程

  • CPU密集型:多进程

  • 混合型:多进程+多线程

Q:线程安全的数据结构有哪些?
A:Python中的Queue、deque(需加锁)、collections.defaultdict(需加锁)等,或使用threading.local实现线程局部存储。

相关文章:

  • 鸿蒙生态新利器:华为ArkUI-X混合开发框架深度解析
  • android contentProvider 踩坑日记
  • uniapp 上传二进制流图片
  • 鸿蒙生态:鸿蒙生态校园行心得
  • Windows下Golang与Nuxt项目宝塔部署指南
  • L1-4、如何写出清晰有目标的 Prompt
  • vscode python 代码无法函数跳转的问题
  • 55、Spring Boot 详细讲义(十一 项目实战)springboot应用的登录功能和权限认证
  • 小刚说C语言刷题——1031 温度转化
  • Ubuntu-Linux中vi / vim编辑文件,保存并退出
  • 云账号安全事件分析:黑客利用RAM子账户发起ECS命令执行攻击
  • 联易融科技:以科技赋能驱动经营反转与价值重估
  • 可吸收聚合物:医疗科技与绿色未来的交汇点
  • K8s:概念、特点、核心组件与简单应用
  • 方案精读:华为智慧园区解决方案【附全文阅读】
  • [创业之路-380]:企业法务 - 企业经营中,企业为什么会虚开増值税发票?哪些是虚开増值税发票的行为?示例?风险?
  • SpringCloud组件—Eureka
  • 【sylar-webserver】重构 增加内存池
  • [盈达科技】GEO(生成式引擎优化)实战指南:从认知重构、技术落地到内容突围的三维战略
  • jQuery — 总结
  • “沉默”的智驾:余承东不喊“遥遥领先”,何小鹏连夜改口 | 电厂
  • 常方舟评《心的表达》|弗洛伊德式精神分析在我们时代的延展
  • 机器人马拉松背后的五大启示:未来社会与机器人的深度融合
  • 央媒聚焦人形机器人:为何发展突然加速?中国制造有何优势?
  • 全国类脑智能产业创新发展推进会在上海召开
  • 第六次国民体质监测展开,高抬腿俯卧撑等新增运动指标受关注