Python编程——进阶知识(多线程)

前言

一、多线程用途

二、线程的创建

三、线程同步

四、线程安全队列:生产者-消费者模式

五、常用线程工具与方法

六、注意事项与常见陷阱

七、怎么使用多线程

小结


前言

在现代软件开发中,多线程 是提升程序响应性、充分利用多核 CPU 和处理 I/O 密集型任务的关键技术。Python 虽然因 GIL(全局解释器锁) 限制了真正的并行计算能力,但在 I/O 密集型场景中,多线程依然能显著提升程序效率和用户体验。

本文将带你深入理解 Python 多线程的核心概念、正确使用方法、同步机制及最佳实践,助你写出高效、安全的并发代码。


一、多线程用途

想象这些场景:

  • Web 服务器同时处理多个用户请求
  • GUI 程序在后台下载文件时保持界面响应
  • 批量爬取多个网页而不逐个等待
  • 实时日志监控与分析

若用单线程实现,程序会在 I/O 操作(如等待网络响应)时完全阻塞 ,浪费 CPU 资源。多线程则允许程序在等待一个任务时,切换到其他任务执行,从而提高整体吞吐量。

注意: Python 多线程适合 I/O 密集型任务 ;对于 CPU 密集型任务 ,应使用 multiprocessing 模块绕过 GIL。


二、线程的创建

1. 函数式

使用 threading.Thread 直接传入目标函数:

python 复制代码
import threading
import time

def worker(name, delay):
    for i in range(3):
        print(f"{name} is working... ({i+1})")
        time.sleep(delay)

# 创建并启动线程
t1 = threading.Thread(target=worker, args=("Worker-1", 1))
t2 = threading.Thread(target=worker, args=("Worker-2", 2))

t1.start()
t2.start()

# 等待线程完成
t1.join()
t2.join()

print("All threads finished.")

2. 面向对象式

继承 threading.Thread 类并重写 run() 方法:

python 复制代码
class MyWorker(threading.Thread):
    def __init__(self, name, delay):
        super().__init__()
        self.name = name
        self.delay = delay
    
    def run(self):
        for i in range(3):
            print(f"{self.name} running... ({i+1})")
            time.sleep(self.delay)

# 使用
t = MyWorker("CustomWorker", 1.5)
t.start()
t.join()

注意 :永远调用 start() 启动线程,而非直接调用 run()


三、线程同步

当多个线程同时修改共享数据 时,可能出现竞态条件(Race Condition),导致结果不可预测。

无同步的操作(产生竞争):

python 复制代码
counter = 0

def increment():
    global counter
    for _ in range(100000):
        counter += 1  # 非原子操作!

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start(); t2.start()
t1.join(); t2.join()
print(counter)  # 可能输出 150000 而非 200000!

解决方案:使用锁(Lock)

python 复制代码
lock = threading.Lock()

def safe_increment():
    global counter
    for _ in range(100000):
        with lock:  # 自动 acquire/release
            counter += 1

# 结果将正确为 200000

建议: 使用 with lock: 语句确保锁被正确释放,即使发生异常。


四、线程安全队列:生产者-消费者模式

queue.Queue 是线程安全的 FIFO 队列,完美解决线程间通信问题。

python 复制代码
import queue
import threading
import time

def producer(q, items):
    for item in items:
        q.put(item)
        print(f"Produced: {item}")
        time.sleep(0.5)

def consumer(q, name):
    while True:
        try:
            item = q.get(timeout=2)  # 等待最多2秒
            print(f"{name} consumed: {item}")
            q.task_done()  # 标记任务完成
        except queue.Empty:
            break

# 创建队列
q = queue.Queue(maxsize=10)

# 启动生产者和消费者
prod = threading.Thread(target=producer, args=(q, ["A", "B", "C"]))
cons1 = threading.Thread(target=consumer, args=(q, "Consumer-1"))
cons2 = threading.Thread(target=consumer, args=(q, "Consumer-2"))

prod.start()
cons1.start()
cons2.start()

prod.join()
q.join()  # 等待所有任务完成
print("All done!")

方法:

  • q.put(item):放入数据
  • q.get():取出数据
  • q.task_done():通知任务完成
  • q.join():阻塞直到队列为空且所有任务完成

五、常用线程工具与方法

方法 作用
threading.current_thread().name 获取当前线程名
threading.active_count() 返回活跃线程数
threading.enumerate() 列出所有活跃线程
t.is_alive() 检查线程是否仍在运行
t.join(timeout) 等待线程结束(可设超时)
python 复制代码
print(f"Main thread: {threading.current_thread().name}")
print(f"Active threads: {threading.active_count()}")

六、注意事项与常见陷阱

1. GIL 的影响

  • Python 的 GIL 确保同一时刻只有一个线程执行 Python 字节码
  • 不影响 I/O 操作 (如 time.sleep(), requests.get()),因为这些操作会释放 GIL
  • CPU 密集型任务无法通过多线程加速 → 改用 multiprocessing

2. 避免死锁

  • 不要嵌套获取多个锁
  • 如需多个锁,始终按相同顺序获取

3. 守护线程(Daemon Threads)

  • 设置 t.daemon = True 可使线程随主线程退出而自动终止
  • 适用于后台监控、日志收集等无需显式关闭的任务
python 复制代码
t = threading.Thread(target=background_task)
t.daemon = True  # 主线程结束时自动 kill
t.start()

七、怎么使用多线程

场景 推荐方案
网络请求、文件读写、数据库查询 多线程 (threading)
图像处理、科学计算、加密解密 改用多进程 (multiprocessing)
简单并行任务 concurrent.futures.ThreadPoolExecutor
需要跨进程共享数据 multiprocessing.Manager

现代替代方案:concurrent.futures

python 复制代码
from concurrent.futures import ThreadPoolExecutor
import requests

def fetch(url):
    return requests.get(url).status_code

urls = ["https://httpbin.org/delay/1"] * 5

with ThreadPoolExecutor(max_workers=3) as executor:
    results = list(executor.map(fetch, urls))
print(results)  # [200, 200, 200, 200, 200]

优势:更简洁、自动管理线程池、支持异步回调。


小结

Python 多线程虽受 GIL 限制,但在 I/O 密集型应用中仍是不可或缺的利器 。掌握线程创建、同步机制(锁、队列)和最佳实践,能让你编写出高效、安全、可维护的并发程序。

"多线程不是银弹,但用对了地方,它就是魔法。"

合理评估任务类型,选择正确的并发模型(线程 vs 进程 vs 异步),才是高性能 Python 编程的真谛。现在,去释放你的程序潜力吧!

相关推荐
Ulyanov2 小时前
基于Pymunk物理引擎的2D坦克对战游戏开发
python·游戏·pygame·pymunk
铉铉这波能秀2 小时前
LeetCode Hot100数据结构背景知识之字典(Dictionary)Python2026新版
数据结构·python·算法·leetcode·字典·dictionary
蜡笔小马2 小时前
10.Boost.Geometry R-tree 空间索引详解
开发语言·c++·算法·r-tree
IOsetting2 小时前
金山云主机添加开机路由
运维·服务器·开发语言·网络·php
程序媛徐师姐2 小时前
Python基于爬虫的网络小说数据分析系统【附源码、文档说明】
爬虫·python·python爬虫·网络小说数据分析系统·pytho网络小说数据分析系统·python爬虫网络小说·python爬虫的网络小说数据
清水白石0082 小时前
深入解析 LRU 缓存:从 `@lru_cache` 到手动实现的完整指南
java·python·spring·缓存
林开落L2 小时前
从零开始学习Protobuf(C++实战版)
开发语言·c++·学习·protobuffer·结构化数据序列化机制
JaydenAI2 小时前
[LangChain之链]LangChain的Chain——由Runnable构建的管道
python·langchain
kali-Myon2 小时前
2025春秋杯网络安全联赛冬季赛-day3
python·安全·web安全·ai·php·web·ctf