在并发编程中,栅栏(Barrier) 和 信号量(Semaphore) 是两种重要的同步原语,常用于协调多个线程或进程之间的执行顺序或资源访问。下面是它们的概念、区别、使用场景对比:
🚧 一、信号量(Semaphore)
🔹 概念:
信号量是一种计数器机制,用来控制多个线程对共享资源的访问。
有两种主要类型:
- 计数信号量:可以有多个许可,常用于控制并发访问数量
- 二元信号量(Binary Semaphore):许可只有 0 和 1,用于实现互斥锁的功能
🔹 用法(伪代码):
Semaphore sem = new Semaphore(3); // 最多允许3个线程同时访问资源
sem.acquire(); // 获取许可,若无则阻塞等待
// 访问资源
sem.release(); // 释放许可
✅ 使用场景:
- 数据库连接池(控制最大连接数)
- 限制 API 并发请求数量
- 控制资源访问(例如:只允许 n 个线程同时写文件)
🧱 二、栅栏(Barrier)
🔹 概念:
栅栏是一种阶段性同步机制,用于让多个线程(或任务)在某个“关口”等待,直到所有线程都到达栅栏后,才统一继续执行。
🔹 用法(伪代码):
barrier = threading.Barrier(3)
def worker():
# 做一些任务
barrier.wait() # 等待其他线程到达
# 所有线程到达后同时继续
✅ 使用场景:
- 多线程并行计算,每个线程处理一部分数据,最后一起合并结果
- 分布式任务阶段同步,如 MapReduce 中的 Map 阶段结束后统一进入 Reduce
🔍 三、栅栏 vs 信号量:对比总结
特性 | 信号量(Semaphore) | 栅栏(Barrier) |
---|---|---|
本质 | 计数器 | 同步点 |
控制资源访问 | ✅ 是 | ❌ 否 |
用于阶段性同步 | ❌ 否 | ✅ 是 |
是否可重用 | ✅ 可以 | ✅ 通常可重用 |
适合场景 | 控制并发数、访问控制 | 线程协作、阶段同步 |
实现复杂度 | 较低 | 中等 |
✅ 示例场景
信号量应用:
限制同时访问数据库的线程数:
semaphore = threading.Semaphore(10)
def db_access():
semaphore.acquire()
try:
access_db()
finally:
semaphore.release()
栅栏应用:
等待所有线程准备好再执行下一步任务:
barrier = threading.Barrier(5)
def step_work():
prepare_data()
barrier.wait()
start_main_task()