信号量与互斥锁的区别与应用
字数 1138 2025-11-02 11:14:05
信号量与互斥锁的区别与应用
描述
信号量(Semaphore)和互斥锁(Mutex)是操作系统和多线程编程中用于同步和互斥的核心工具。它们的主要区别在于:
- 互斥锁:用于互斥访问,保证同一时间只有一个线程能进入临界区(如修改共享变量)。
- 信号量:本质是计数器,可控制多个线程的并发数量(如限制资源访问的线程数)。
常见面试题会要求解释两者区别,并举例说明应用场景。
解题过程
-
核心概念理解
- 互斥锁:
- 只有“锁定”和“解锁”两种状态。
- 谁加锁,谁解锁(所有权概念)。
- 例如,线程A锁定后,其他线程需等待A解锁才能进入。
- 信号量:
- 是一个整数计数器,通过
wait()(P操作)和signal()(V操作)修改值。 - 没有所有权限制,任意线程可执行
signal()。 - 分类:
- 二值信号量(值域0/1):功能类似互斥锁,但无所有权。
- 计数信号量(值≥0):控制N个同类资源的访问。
- 是一个整数计数器,通过
- 互斥锁:
-
关键区别对比
区别点 互斥锁 信号量 核心用途 互斥(一对一竞争) 同步(控制多线程协作) 计数器特性 无计数器,仅二元状态 有计数器,可表示资源数量 所有权 有(锁的持有者必须解锁) 无(任意线程可操作信号量) 典型场景 保护临界区(如共享变量) 限流、任务调度(如线程池) -
实例说明
- 互斥锁场景:
多个线程同时写同一个文件,需用互斥锁保证某一时刻仅一个线程写入。lock = threading.Lock() lock.acquire() # 加锁 # 写文件操作(临界区) lock.release() # 解锁 - 信号量场景:
数据库连接池有10个连接,需限制同时访问的线程数。semaphore = threading.Semaphore(10) # 初始值=10 semaphore.acquire() # 获取连接(计数器-1) # 使用数据库连接 semaphore.release() # 释放连接(计数器+1)
- 互斥锁场景:
-
常见误区澄清
- 二值信号量≠互斥锁:
- 互斥锁有优先级继承(防止优先级反转),二值信号量无此机制。
- 互斥锁必须由持有者解锁,二值信号量可由其他线程释放。
- 选择原则:
- 若需严格互斥(如修改全局变量),用互斥锁。
- 若需协调多任务(如生产者-消费者),用信号量。
- 二值信号量≠互斥锁:
-
扩展:信号量的底层实现
信号量通过原子操作(如CAS)和线程阻塞队列实现:- 执行
wait()时,若计数器>0,则减1并继续;否则线程阻塞并加入队列。 - 执行
signal()时,计数器加1,并唤醒队列中一个阻塞线程。
- 执行
总结
信号量是更通用的同步工具(可实现互斥锁),但互斥锁因所有权特性更安全。实际开发中,应根据场景选择:互斥锁用于资源独占,信号量用于流量控制或协作逻辑。