从初级到高级的介绍一下 python 的threading用法
threading 是 Python 用来处理多线程的工具,简单说就是让程序同时干多件事儿,比如一边下载文件一边播放音乐。
初级:啥是线程?怎么跑起来?
1. 理解线程:多干点活儿
想象你在饭店点餐:普通程序(单线程)就像服务员只盯着你,等你吃完再去招呼别人;多线程呢,就像饭店有好几个服务员,同时招呼好几桌客人。threading 就是帮你“雇”几个服务员,让程序并行干活。
2. 最简单的起步:跑个线程
Python 自带 threading 模块,不用装啥,直接开干。咱先写个最简单的多线程代码:
import threading
import time
def say_hello():
print("Hello")
time.sleep(1) # 假装干活1秒
print("World")
# 创建线程
t = threading.Thread(target=say_hello)
# 启动线程
t.start()
# 主线程继续干活
print("主线程不等你,我先跑了")
跑一下,输出可能是:
Hello
主线程不等你,我先跑了
(等1秒)
World
threading.Thread(target=函数):创建一个线程,告诉它要跑哪个函数。
start():启动线程,让它开始干活。
主线程(程序默认的线程)和新线程一起跑,谁先谁后看运气。
3. 等线程干完:join
有时候你想让主线程等等其他线程干完再走,用 join():
import threading
import time
def task():
print("任务开始")
time.sleep(2)
print("任务结束")
t = threading.Thread(target=task)
t.start()
t.join() # 主线程在这等着
print("线程干完了,我再跑")
输出:
任务开始
(等2秒)
任务结束
线程干完了,我再跑
join():让主线程等着,直到这个线程干完。
4. 多线程一起跑
再加几个线程试试:
import threading
import time
def worker(name):
print(f"工人 {name} 开始干活")
time.sleep(1)
print(f"工人 {name} 干完了")
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join() # 等所有线程干完
print("所有工人下班了")
输出可能是:
工人 0 开始干活
工人 1 开始干活
工人 2 开始干活
(等1秒)
工人 0 干完了
工人 1 干完了
工人 2 干完了
所有工人下班了
args=(i,):给线程函数传参数。
多个线程几乎同时启动,总耗时是1秒,而不是3秒(1+1+1)。
中级:整点实用的活儿
1. 线程安全的共享数据:锁
多线程一起跑有个坑:如果大家同时改同一块数据,可能乱套。比如计数器:
import threading
counter = 0
def add():
global counter
for _ in range(100000):
counter += 1
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()
print("计数器结果:", counter)
你可能期待结果是 200000(两个线程各加 100000),但实际可能是 180000 之类的。为什么?因为线程抢着改 counter,会“撞车”。
- 解决办法:加锁*
import threading
counter = 0
lock = threading.Lock() # 创建锁
def add():
global counter
for _ in range(100000):
with lock: # 上锁
counter += 1
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()
print("计数器结果:", counter)
输出:
计数器结果:200000
threading.Lock():创建一个锁。 with lock
threading 的 Event 的用法
啥是 Event?
想象你在组织一个饭局,大家都到了才能开吃。Event 就像饭店老板娘,等所有人到齐,她喊一声“开饭啦”,大家才动筷子。在线程里,Event 是一个信号工具:
默认是“红灯”(未设置状态,False),线程看到红灯就等着。 有人把信号设成“绿灯”(True),等着的人就知道可以干活了。
基础用法:红灯绿灯 1. 创建和简单使用
import threading
import time
# 创建一个 Event 对象
event = threading.Event()
def waiter():
print("小弟等着老板喊开工")
event.wait() # 等绿灯
print("老板喊了,小弟开干!")
def boss():
print("老板先忙别的")
time.sleep(2) # 假装忙2秒
print("老板喊:开工啦!")
event.set() # 红灯变绿灯
# 创建线程
t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=boss)
t1.start()
t2.start()
t1.join()
t2.join()
输出:
小弟等着老板喊开工
老板先忙别的
(等2秒)
老板喊:开工啦!
小弟开干!
threading.Event():创建一个信号对象,默认是红灯(False)。
event.wait():线程在这等着,直到信号变绿(True)。
event.set():把信号设成绿灯,通知所有等着的人。
2. 检查信号状态
可以用 is_set() 看看当前是红灯还是绿灯:
import threading
event = threading.Event()
print("信号状态:", event.is_set()) # False,红灯
event.set()
print("信号状态:", event.is_set()) # True,绿灯
输出:
信号状态:False
信号状态:True
3. 关掉绿灯:clear
信号变绿后,可以用 clear() 把它变回红灯:
import threading
import time
event = threading.Event()
def worker():
print("工人等着信号")
event.wait()
print("信号绿了,干活啦")
time.sleep(1)
print("活干完了")
t = threading.Thread(target=worker)
t.start()
time.sleep(1)
print("老板点绿灯")
event.set() # 绿灯
time.sleep(2)
print("老板关绿灯")
event.clear() # 变回红灯
print("信号状态:", event.is_set())
t.join()
输出:
工人等着信号
(等1秒)
老板点绿灯
信号绿了,干活啦
(等1秒)
活干完了
老板关绿灯
信号状态:False
clear():把绿灯变回红灯,适合重复使用的场景。
实用场景:协调多线程
1. 等所有人准备好再开工
比如三个工人得等老板一声令下一起干活:
import threading
import time
event = threading.Event()
def worker(name):
print(f"工人 {name} 准备好了")
event.wait() # 等信号
print(f"工人 {name} 开始干活")
# 创建3个工人线程
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
time.sleep(1) # 假装老板磨蹭一下
print("老板喊:全体开工!")
event.set() # 绿灯,所有人一起干
for t in threads:
t.join()
输出:
工人 0 准备好了
工人 1 准备好了
工人 2 准备好了
(等1秒)
老板喊:全体开工!
工人 0 开始干活
工人 1 开始干活
工人 2 开始干活
一个 Event 可以控制多个线程,信号一变,所有等着的人都动。
2. 加个超时:别傻等
有时候不想无限等,可以给 wait() 加个超时:
import threading
import time
event = threading.Event()
def lazy_worker():
print("懒工人等着信号")
if event.wait(timeout=2): # 最多等2秒
print("信号来了,干活")
else:
print("等烦了,不干了")
t = threading.Thread(target=lazy_worker)
t.start()
time.sleep(3) # 老板故意拖延
print("老板姗姗来迟")
event.set()
t.join()
输出:
懒工人等着信号
(等2秒)
等烦了,不干了
(再等1秒)
老板姗姗来迟
wait(timeout=秒数):等够时间没绿灯就返回 False,继续往下走。
高级用法:生产者-消费者模式
Event 在生产者和消费者场景里很常见,比如一个线程生产数据,另一个消费数据:
import threading
import time
import random
event = threading.Event()
data = None
def producer():
global data
for i in range(3):
time.sleep(1)
data = f"货物 {i}"
print(f"生产者生产了 {data}")
event.set() # 通知消费者
event.wait() # 等消费者拿走
event.clear() # 重置信号
def consumer():
for _ in range(3):
event.wait() # 等生产者信号
print(f"消费者拿到了 {data}")
time.sleep(random.uniform(0.5, 1)) # 模拟消费时间
event.set() # 通知生产者我拿完了
event.clear() # 重置信号
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()
输出(时间随机):
生产者生产了 货物 0
消费者拿到了 货物 0
生产者生产了 货物 1
消费者拿到了 货物 1
生产者生产了 货物 2
消费者拿到了 货物 2
生产者用 set() 通知有货,消费者用 set() 通知拿完了。 用 clear() 重置信号,确保每次交易干净利落。
文档信息
版权声明:可自由转载(请注明转载出处)-非商用-非衍生
发表时间:2025年7月1日 10:23