程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

Python threading库详解:让程序"多线程"飞起来

hfteth 2025-08-06 19:44:30 技术文章 1 ℃

在当今这个多任务处理的时代,让程序能够"同时"做多件事情变得越来越重要。threading库就是Python中实现多线程编程的利器。

一、什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。简单来说:

  • 一个进程可以包含多个线程
  • 线程共享进程的内存空间
  • 线程比进程更轻量,创建和切换开销更小

https://example.com/thread_vs_process.png

二、为什么需要多线程?

想象一下你在厨房做饭:

  • 单线程:先烧水,水开了再切菜,然后炒菜——效率低下
  • 多线程:一边烧水一边切菜——效率大大提高

在程序中,多线程特别适合I/O密集型任务,比如:

  • 网络请求
  • 文件读写
  • 数据库操作

三、threading基础用法

1. 创建线程

Python的threading模块提供了Thread类来创建和管理线程。看个简单例子:

import threading
import time

def task(name):
    print(f"任务 {name} 开始")
    time.sleep(2)  # 模拟耗时操作
    print(f"任务 {name} 完成")

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

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

# 等待线程结束
t1.join()
t2.join()

print("所有任务完成")

输出可能是:

任务 A 开始
任务 B 开始
(约2秒后)
任务 A 完成
任务 B 完成
所有任务完成

2. 线程的生命周期

  • 新建:创建Thread对象
  • 就绪:调用start()后
  • 运行:被调度器选中
  • 阻塞:遇到I/O操作或sleep等
  • 终止:run()方法执行完毕

四、线程同步

当多个线程共享数据时,可能会产生竞争条件。threading提供了多种同步机制:

1. Lock(锁)

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        lock.acquire()  # 获取锁
        counter += 1
        lock.release()  # 释放锁

threads = []
for _ in range(5):
    t = threading.Thread(target=increment)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print(f"最终计数器值: {counter}")  # 应该是500000

2. RLock(可重入锁)

同一个线程可以多次acquire同一个RLock

rlock = threading.RLock()

def func():
    with rlock:  # 第一次获取锁
        with rlock:  # 第二次获取同一个锁
            print("成功获取锁")

3. Condition(条件变量)

用于线程间的通信

import threading

def consumer(cond):
    with cond:
        print("消费者等待中...")
        cond.wait()  # 等待通知
        print("消费者收到通知,继续执行")

def producer(cond):
    with cond:
        print("生产者准备通知")
        cond.notifyAll()  # 通知所有等待的线程
        print("已通知")

condition = threading.Condition()
t1 = threading.Thread(name='consumer', target=consumer, args=(condition,))
t2 = threading.Thread(name='producer', target=producer, args=(condition,))

t1.start()
t2.start()

t1.join()
t2.join()

五、线程池ThreadPoolExecutor

Python 3.2+引入了concurrent.futures模块,提供了更高级的线程池接口:

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
    time.sleep(1)
    return n * n

with ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(task, i) for i in range(5)]
    for future in concurrent.futures.as_completed(futures):
        print(future.result())

六、线程的局限性

需要注意的是,由于Python的GIL(全局解释器锁),多线程在CPU密集型任务上并不能真正并行执行。对于CPU密集型任务,建议使用multiprocessing模块。

七、实际应用案例

1. 多线程下载文件

import threading
import requests

def download(url, filename):
    print(f"开始下载 {filename}")
    response = requests.get(url, stream=True)
    with open(filename, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    print(f"{filename} 下载完成")

urls = [
    ("https://example.com/file1.zip", "file1.zip"),
    ("https://example.com/file2.zip", "file2.zip"),
    ("https://example.com/file3.zip", "file3.zip")
]

threads = []
for url, filename in urls:
    t = threading.Thread(target=download, args=(url, filename))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print("所有文件下载完成")

2. 生产者-消费者模型

import threading
import queue
import random
import time

def producer(q, name):
    for i in range(5):
        item = f"{name}-产品{i}"
        q.put(item)
        print(f"{name} 生产了 {item}")
        time.sleep(random.random())

def consumer(q, name):
    while True:
        item = q.get()
        if item is None:  # 终止信号
            break
        print(f"{name} 消费了 {item}")
        time.sleep(random.random() * 2)
        q.task_done()

q = queue.Queue()
producers = []
consumers = []

# 创建2个生产者
for i in range(2):
    p = threading.Thread(target=producer, args=(q, f"生产者{i}"))
    p.start()
    producers.append(p)

# 创建3个消费者
for i in range(3):
    c = threading.Thread(target=consumer, args=(q, f"消费者{i}"))
    c.start()
    consumers.append(c)

# 等待生产者完成
for p in producers:
    p.join()

# 发送终止信号给消费者
for _ in consumers:
    q.put(None)

# 等待消费者完成
for c in consumers:
    c.join()

print("生产消费结束")

八、线程最佳实践

  1. 避免使用全局变量:线程间共享数据容易引发竞争条件
  2. 合理使用锁:锁会降低性能,只在必要时使用
  3. 避免死锁:确保锁总是能被释放(使用with语句)
  4. 控制线程数量:太多线程会导致上下文切换开销增大
  5. 考虑使用队列:queue模块提供了线程安全的队列实现

九、总结

Python的threading模块为多线程编程提供了强大的支持,特别适合I/O密集型任务。通过合理使用线程同步机制,我们可以构建高效、可靠的多线程应用。不过也要记住它的局限性,在CPU密集型任务中考虑使用多进程。

Tags:

最近发表
标签列表