1、线程:threading.Thread
1)线程 Thread 在模块 threading 中,导入模块
python
import threading
2)Thread 构造函数的参数
python
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
- group 必须为 None;预留参数,用于线程组,目前未实现。
- target:是由 run() 方法调用的可执行对象(函数)。
- name:线程名称。默认会自动生成唯一名称,格式为 "Thread-N"。
- args:调用 target 时的参数(列表/元组),默认空元组 ();
多个参数的写法(a,b,c,),单个参数务必写逗号(self.server,) - kwargs:调用 target 时用的关键字参数字典,默认空字典 {}。
- daemon:是否为守护线程:True=主线程退出时自动结束此线程,否则主线程阻塞,等待该线程退出
3)用法一:函数传递给target
python
import threading
def work(name):
print(f"线程 {name} ")
t1 = threading.Thread(target=work, args=("A",))
t1.start()
t1.join()
- start():启动线程,开始运行传递给target的函数
- join():主线程等待该线程执行完毕再继续往下走
4)用法二:继承Thread,重载run函数
python
import threading
class MyThread(threading.Thread):
def run(self):
print("自定义线程")
t = MyThread()
t.start()
t.join()
2、线程同步
2.1 Lock 互斥锁
1)一般写法
python
lock = threading.Lock()
lock.acquire() 加锁
......临界区代码......
lock.release() 释放锁
2)自动释放锁写法
python
with lock:
......临界区代码......
pass
pass 表示空语句,用来满足python缩进代码块
2.2 RLock 可重入锁
避免同一线程重复加锁导致死锁
python
def func_a():
with rlock:
func_b() # 嵌套再次加锁,RLock 支持
def func_b():
with rlock:
print("临界区执行")
2.3 Semaphore 信号量
先创建一个计数器,并设置号初始值;
- P 操作 (acquire)一次,申请一个资源,计数器 -1;如果计数器的值为0,不能再-1,阻塞在这里,直到有资源释放,计数器变为正值;
- V 操作 (release)一次,释放一个资源,计数器 +1;如果计数器的值大于0,会唤醒阻塞的线程
- 多次V 操作,超过初始值会怎么样?不会报错,但是逻辑上会超出设计意图,建议使用 BoundedSemaphore 有界信号量,超过初始值会报异常 ValueError 来提示
python
import threading
import time
empty = threading.Semaphore(5) # 空槽位数量
full = threading.Semaphore(0) # 已生产产品数量
buffer = []
def producer():
for i in range(10):
empty.acquire()
buffer.append(i)
print(f"生产: {i}, buffer={buffer}")
full.release()
time.sleep(0.2)
def consumer():
for i in range(10):
full.acquire()
item = buffer.pop(0)
print(f"消费: {item}, buffer={buffer}")
empty.release()
time.sleep(0.5)
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()
2.4 Condition 条件变量
Condition 内部 = 1 把锁(默认 RLock) + 1 个等待线程队列(_waiters = _deque())
- 使用 Condition 创建的对象是共享对象,生产者线程、消费者线程都引用同一个实例
- 所有 cond.wait()的线程都会进入同一个等待队列(_waiters )
- notify() / notify_all() 就是从这个队列里唤醒线程(_waiters )
1)生产者等待条件:缓冲区满
python
while len(buffer) >= MAX_SIZE:
cond.wait()
2)消费者等待条件:缓冲区空
python
while len(buffer) == 0:
cond.wait()
3)当 notify_all() 唤醒所有等待线程:
- 生产者线程醒来:检查 while 条件,如果缓冲区仍然满,再次 wait 回去
- 消费者线程醒来:检查 while 条件,如果缓冲区有数据,正常消费
4)全程只使用一个 Condition 即可
生产者、消费者的完整示例
python
import threading
import time
# 创建条件变量
cond = threading.Condition()
buffer = []
MAX_SIZE = 3 # 缓冲区最大容量
# 生产者
def producer():
for i in range(10):
with cond:
# 缓冲区满则等待
while len(buffer) >= MAX_SIZE:
print(f"缓冲区已满,生产者等待...")
cond.wait()
# 放入产品
buffer.append(i)
print(f"生产 {i}, buffer={buffer}")
cond.notify_all() # 通知消费者
time.sleep(0.3)
# 消费者
def consumer():
for _ in range(10):
with cond:
# 缓冲区空则等待
while len(buffer) == 0:
print(f"缓冲区为空,消费者等待...")
cond.wait()
# 取出产品
item = buffer.pop(0)
print(f"消费 {item}, buffer={buffer}")
cond.notify_all() # 通知生产者
time.sleep(0.5)
t_prod = threading.Thread(target=producer)
t_cons = threading.Thread(target=consumer)
t_prod.start()
t_cons.start()
t_prod.join()
t_cons.join()
2.5 Event 事件(全局信号)
用途:主线程统一发启动信号,批量启动工作线程
- 一行 wait()等待全局信号,适合启停控制
- 一次性广播通知 set() ,批量唤醒所有等待线程,代码干净
python
import threading
import time
def worker(e, idx):
print(f"线程{idx}:等待全局启动信号")
e.wait() # 阻塞直到 set()
print(f"线程{idx}:收到信号,开始执行任务")
time.sleep(1)
print(f"线程{idx}:任务结束")
if __name__ == "__main__":
start_event = threading.Event()
threads = [threading.Thread(target=worker, args=(start_event, i)) for i in range(3)]
for t in threads:
t.start()
time.sleep(2)
print("主线程:发送全局启动信号")
start_event.set() # 全部线程同时放行
for t in threads:
t.join()
2.6 Barrier 屏障
用途:原生实现 N 线程卡点同步:保证全部 N 线程就绪后,才一起继续执行,解决多线程阶段同步;
并行仿真、数值计算:所有线程完成数据加载、初始化,再同步开始主计算
示例
python
import threading
import time
barrier = threading.Barrier(parties=3, action=lambda: print("=== 全部线程就绪,同步开始计算 ==="))
def worker(idx):
print(f"线程{idx}:正在加载初始化数据...")
time.sleep(idx * 0.5) # 每个线程初始化耗时不同
print(f"线程{idx}:到达屏障点,等待其余线程")
try:
barrier.wait(timeout=5) # 卡点等待3个线程集齐
except threading.BrokenBarrierError:
print(f"线程{idx}:屏障异常中断")
return
print(f"线程{idx}:同步执行正式计算")
time.sleep(1)
if __name__ == "__main__":
threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
print("本轮计算完成")
输出
bash
线程0:正在加载初始化数据...
线程0:到达屏障点,等待其余线程
线程1:正在加载初始化数据...
线程2:正在加载初始化数据...
线程1:到达屏障点,等待其余线程
线程2:到达屏障点,等待其余线程
=== 全部线程就绪,同步开始计算 ===
线程0:同步执行正式计算
线程2:同步执行正式计算
线程1:同步执行正式计算
本轮计算完成