threading,一个并发的 Python 库!
一、库的简介:并发执行的多面手
在日常开发中,你会遇到这样的场景:一个程序需要一边下载文件,一边响应用户的点击操作;或者一个监控工具需要同时监听多个设备的状态,而不是挨个等待。如果所有任务都按顺序执行,那么耗时的操作(如网络请求、文件读写)就会阻塞整个程序,用户界面卡死,体验极差。Python 的 threading 标准库提供了多线程编程的支持,让你可以在同一个进程内创建多个线程,并发地执行不同的任务。在实际生活中,threading 可以驱动一个 GUI 程序的后台计算(比如文本编辑器自动保存文档时不卡住输入),可以为爬虫程序同时请求多个网页,也可以实现一个定时器在特定时间执行回调。需要注意的是,由于 Python 全局解释器锁(GIL)的存在,threading 对于 CPU 密集型任务(如大量数值计算)并不能提升性能,但对于 I/O 密集型任务(网络请求、磁盘读写、数据库查询),它可以极大地提高程序的吞吐量和响应速度。
二、安装 threading
threading 是 Python 的内置标准库,无需任何 pip install 命令,导入即可使用:
python
import threading
三、基本用法
以下代码假设已经导入 import threading。
第一步:创建并启动一个线程
使用 threading.Thread(target=函数名, args=(参数,)) 创建线程,调用 start() 启动。
python
import threading
import time
def print_numbers():
for i in range(5):
time.sleep(0.5)
print(f"子线程: {i}")
t = threading.Thread(target=print_numbers)
t.start() # 启动子线程
print("主线程继续执行...")
t.join() # 等待子线程结束
print("程序结束")
第二步:传递参数给线程函数
通过 args 传递位置参数,kwargs 传递关键字参数。
python
def greet(name, delay=1):
time.sleep(delay)
print(f"Hello, {name}")
t1 = threading.Thread(target=greet, args=("Alice",), kwargs={"delay": 2})
t1.start()
第三步:守护线程(Daemon Thread)
将线程设置为守护线程(daemon=True),主线程结束时,守护线程会立即被强制终止,不等待其完成。常用于后台任务,如心跳检测、日志刷新。
python
def background_task():
while True:
time.sleep(1)
print("后台运行...")
t = threading.Thread(target=background_task, daemon=True)
t.start()
time.sleep(3)
print("主线程退出,守护线程随之结束")
第四步:获取当前线程信息
threading.current_thread() 可以获取当前线程对象,常用于调试。
python
def worker():
print(f"线程 {threading.current_thread().name} 开始工作")
t1 = threading.Thread(target=worker, name="Worker-1")
t2 = threading.Thread(target=worker, name="Worker-2")
t1.start()
t2.start()
四、高级用法
1. 线程同步 —— Lock
多个线程同时修改共享变量会导致数据竞争。使用 threading.Lock 保证同一时刻只有一个线程执行临界区代码。
python
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
with lock: # 上下文管理器自动获取和释放锁
counter += 1
threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # 应为 500000,不加锁会远小于此值
2. RLock —— 可重入锁
同一个线程可以多次获取 RLock,避免死锁。
python
rlock = threading.RLock()
def recursive_func(n):
with rlock:
if n > 0:
recursive_func(n-1)
3. Event —— 线程间信号通信
一个线程等待 Event 被设置,另一个线程设置它,用于协调启动或停止任务。
python
event = threading.Event()
def waiter():
print("等待事件...")
event.wait()
print("事件已触发,继续执行")
def setter():
time.sleep(2)
print("设置事件")
event.set()
threading.Thread(target=waiter).start()
threading.Thread(target=setter).start()
五、实际应用场景案例
场景一:多线程下载网页(I/O 并发)
假设你需要从多个 URL 下载内容,使用线程池可以显著减少总耗时。这里展示一个简单的线程池实现:
python
import threading
import requests # 需要安装 pip install requests
import time
urls = [
"https://www.example.com",
"https://www.python.org",
"https://www.github.com",
] * 5 # 共15个任务
results = {}
lock = threading.Lock()
def download(url):
try:
resp = requests.get(url, timeout=5)
with lock:
results[url] = len(resp.text)
print(f"完成: {url} 大小 {len(resp.text)}")
except Exception as e:
print(f"失败: {url} - {e}")
start = time.time()
threads = []
for url in urls:
t = threading.Thread(target=download, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join()
print(f"总共下载 {len(results)} 个页面,耗时 {time.time()-start:.2f} 秒")
场景二:生产者-消费者模式(使用 Queue)
queue.Queue 是线程安全的队列,适合在线程之间传递数据。下面的例子中,生产者不断生成任务,多个消费者并发处理。
python
import threading
import queue
import time
import random
q = queue.Queue(maxsize=10)
def producer():
for i in range(20):
item = f"任务-{i}"
q.put(item)
print(f"生产: {item}")
time.sleep(random.uniform(0.1, 0.3))
q.put(None) # 结束信号
def consumer(name):
while True:
item = q.get()
if item is None:
q.put(None) # 传递给其他消费者
break
print(f" 消费者 {name} 处理: {item}")
time.sleep(random.uniform(0.2, 0.5))
q.task_done()
prod_thread = threading.Thread(target=producer)
cons_threads = [threading.Thread(target=consumer, args=(f"C{i}",)) for i in range(3)]
prod_thread.start()
for t in cons_threads:
t.start()
prod_thread.join()
for t in cons_threads:
t.join()
print("所有任务完成")
场景三:定时器(Timer)—— 延时执行任务
threading.Timer 可以在指定时间后执行一个函数,非常适合做延迟提醒、定时重试等。
python
def remind(message):
print(f"提醒: {message}")
print("设置5秒后提醒")
timer = threading.Timer(5.0, remind, args=("该休息一下了",))
timer.start()
# 也可以取消定时器
# timer.cancel()
六、结尾互动
threading 是 Python 并发编程的基石之一。它为 I/O 密集型任务提供了简单优雅的并发模型,让程序能够充分利用等待时间去做其他事情。但要注意,多线程并非万能药——GIL 限制了 CPU 密集型任务的并行能力,此时应考虑 multiprocessing 或异步编程。同时,线程间的数据竞争和死锁也需要细心规避,合理使用锁、队列等同步工具是写出健壮多线程程序的关键。
你是否有过用 threading 加速爬虫或后台任务的经验?或者曾踩过死锁、变量竞争的大坑?欢迎在评论区分享你的故事和心得。不妨试试用 threading 写一个“同时下载多张图片并压缩”的小工具,感受并发带来的效率提升!
更多推荐



所有评论(0)