Python 多线程教程

多线程是实现并发编程的重要方式,适用于I/O 密集型任务(如网络请求、文件读写、数据库操作),能有效提升程序执行效率。Python 中主要通过 threading 模块实现多线程,本文将从基础到进阶全面讲解。

一、核心概念

1. 线程与进程

  • 进程:程序的一次运行实例,有独立的内存空间,进程间通信复杂。
  • 线程:进程内的执行单元,共享进程内存,切换开销远小于进程。
  • Python 限制:由于 GIL(全局解释器锁),同一时刻只有一个线程执行 Python 字节码,因此多线程对 CPU 密集型任务提速有限(需用多进程),但对 I/O 密集型任务依然高效。

2. 适用场景

  • ✅ 适合:网络爬虫、API 调用、文件读写、数据库查询等 I/O 密集型任务。
  • ❌ 不适合:大规模计算、数据处理等 CPU 密集型任务(推荐用 multiprocessing 多进程)。

二、基础使用:threading 模块

1. 最简单的多线程示例

通过 threading.Thread 创建线程,target 指定线程执行的函数,start() 启动线程。
python
 
运行
import threading
import time

# 定义线程要执行的函数
def task(name, delay):
    print(f"线程 {name} 启动")
    time.sleep(delay)  # 模拟I/O操作(如网络请求)
    print(f"线程 {name} 结束")

# 创建线程
t1 = threading.Thread(target=task, args=("A", 2))
t2 = threading.Thread(target=task, args=("B", 1))

# 启动线程
t1.start()
t2.start()

# 等待所有线程结束(可选)
t1.join()
t2.join()

print("所有线程执行完成")
 
输出结果(顺序可能因调度变化):
plaintext
线程 A 启动
线程 B 启动
线程 B 结束
线程 A 结束
所有线程执行完成
 

2. 关键方法说明

方法 作用
Thread() 创建线程对象,参数:target(执行函数)、args(参数元组)、name(线程名)
start() 启动线程(调用函数),不可重复调用
join(timeout) 等待线程结束,timeout 为可选超时时间(秒)
is_alive() 判断线程是否还在运行
getName()/setName() 获取 / 设置线程名(也可通过 name 属性)

三、进阶用法

1. 自定义线程类

继承 threading.Thread 并重写 run() 方法,适合复杂逻辑的线程。
python
 
运行
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, name, delay):
        super().__init__()  # 必须调用父类构造方法
        self.name = name
        self.delay = delay

    # 重写run方法,线程启动时执行
    def run(self):
        print(f"自定义线程 {self.name} 启动")
        time.sleep(self.delay)
        print(f"自定义线程 {self.name} 结束")

# 创建并启动线程
t1 = MyThread("X", 3)
t2 = MyThread("Y", 2)

t1.start()
t2.start()

t1.join()
t2.join()
print("自定义线程执行完成")
 

2. 线程同步:解决资源竞争

多个线程共享数据时,可能出现资源竞争(如同时修改一个变量),需用 Lock 加锁保证原子操作。

问题示例(资源竞争):

python
 
运行
import threading

count = 0  # 共享变量

def increment():
    global count
    for _ in range(100000):
        count += 1  # 非原子操作,可能被打断

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)

t1.start()
t2.start()
t1.join()
t2.join()

print(f"预期结果:200000,实际结果:{count}")  # 实际结果小于200000
 

解决方案(Lock 加锁):

python
 
运行
import threading

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

def increment():
    global count
    for _ in range(100000):
        with lock:  # 自动获取锁、释放锁(推荐)
            count += 1
    # 等价写法:
    # lock.acquire()  # 获取锁
    # try:
    #     count += 1
    # finally:
    #     lock.release()  # 释放锁(必须保证释放)

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)

t1.start()
t2.start()
t1.join()
t2.join()

print(f"预期结果:200000,实际结果:{count}")  # 正确输出200000
 

3. 线程池:高效管理线程

当需要创建大量线程时,手动创建线程效率低,推荐使用 concurrent.futures.ThreadPoolExecutor 线程池。
python
 
运行
import concurrent.futures
import time

def task(name, delay):
    print(f"线程 {name} 启动")
    time.sleep(delay)
    print(f"线程 {name} 结束")
    return f"{name} 完成"

# 创建线程池(最大线程数为2)
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
    # 提交任务
    future1 = executor.submit(task, "A", 2)
    future2 = executor.submit(task, "B", 1)

    # 获取任务结果(阻塞直到完成)
    print(future1.result())
    print(future2.result())

# 批量提交任务
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    tasks = [("C", 1), ("D", 2), ("E", 1)]
    # map方式:按任务顺序返回结果
    results = executor.map(lambda x: task(x[0], x[1]), tasks)
    for res in results:
        print(res)
 

4. 守护线程(后台线程)

守护线程会随主线程结束而终止,适合做后台任务(如监控、日志)。通过 setDaemon(True) 设置(必须在 start() 前)。
python
 
运行
 
import threading
import time

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

# 创建守护线程
daemon_thread = threading.Thread(target=daemon_task)
daemon_thread.setDaemon(True)  # 设置为守护线程
daemon_thread.start()

# 主线程运行3秒后结束,守护线程也会被终止
time.sleep(3)
print("主线程结束")
 

四、常见问题与注意事项

1. GIL 影响

  • Python 的 GIL 导致同一时刻只有一个线程执行 Python 代码,多线程无法利用多核 CPU。
  • 解决 CPU 密集型任务:使用 multiprocessing 多进程,或用 C 扩展、PyPy 等。

2. 线程安全

  • 避免共享可变数据(如列表、字典),若必须共享,需用锁(Lock)、信号量(Semaphore)等同步机制。
  • 常用同步工具:Lock(互斥锁)、RLock(可重入锁)、Semaphore(信号量)、Event(事件)、Condition(条件变量)。

3. 避免死锁

  • 死锁原因:多个线程互相等待对方释放锁。
  • 避免方法:
    • 按固定顺序获取锁;
    • 设置锁的超时时间;
    • 尽量减少锁的嵌套。

4. 线程退出

  • 线程正常退出:函数执行完成;
  • 强制退出:不推荐(可能导致资源泄漏),建议通过标志位让线程自行退出。

五、总结

  • Python 多线程适合 I/O 密集型任务,核心模块为 threading
  • 线程同步用 Lock 解决资源竞争问题;
  • 大量线程推荐使用 ThreadPoolExecutor 线程池;
  • 注意 GIL 限制,CPU 密集型任务优先用多进程。
posted @ 2026-01-03 10:46  福寿螺888  阅读(234)  评论(0)    收藏  举报