互斥量和信号量的区别

互斥量和信号量的区别

1. 互斥量用于线程的互斥,信号量用于线程的同步。

这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

2. 互斥量值只能为0/1,信号量值可以为非负整数

也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。

3. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到

互斥量(Mutex)

 

互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。

clip_image001

 

Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex主要的作用是用于互斥。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0。

Mutex可以被抽象为四个操作:

- 创建 Create

- 加锁 Lock

- 解锁 Unlock

- 销毁 Destroy

Mutex被创建时可以有初始值,表示Mutex被创建后,是锁定状态还是空闲状态。在同一个线程中,为了防止死锁,系统不允许连续两次对Mutex加锁(系统一般会在第二次调用立刻返回)。也就是说,加锁和解锁这两个对应的操作,需要在同一个线程中完成。

不同操作系统中提供的Mutex函数:

动作/系统

Win32

Linyx

Solaris

创建

CreateMutex

pthread_mutex_init

mutex_init

加锁

WaitForSingleObject

pthread_mutex_lock

mutex_lock

解锁

ReleaseMutex

pthread_mutex_unlock

mutex_unlock

销毁

CloseHandle

pthread_mutex_destroy

mutex_destroy

 

 

死锁主要发生在有多个依赖锁存在时, 会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生. 如何避免死锁是使用互斥量应该格外注意的东西。

  总体来讲, 有几个不成文的基本原则:

  对共享资源操作前一定要获得锁。

  完成操作以后一定要释放锁。

  尽量短时间地占用锁。

  如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。

  线程错误返回时应该释放它所获得的锁。

 

也许还有读者好奇,“挂起等待”和“唤醒等待线程”的操作如何实现?每个Mutex有一个等待队列,一个线程要在Mutex上挂起等待,首先在把自己加入等待队列中,然后置线程状态为睡眠,然后调用调度器函数切换到别的线程。一个线程要唤醒等待队列中的其它线程,只需从等待队列中取出一项,把它的状态从睡眠改为就绪,加入就绪队列,那么下次调度器函数执行时就有可能切换到被唤醒的线程

 

一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此就永远处于挂起等待状态了,这叫做死锁(Deadlock)。另一种典型的死锁情形是这样:线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。不难想象,如果涉及到更多的线程和更多的锁,有没有可能死锁的问题将会变得复杂和难以判断。

 

信号量

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

信号量可以分为几类:

² 二进制信号量(binary semaphore):只允许信号量取0或1值,其同时只能被一个线程获取。

² 整型信号量(integer semaphore):信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。

² 记录型信号量(record semaphore):每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。

信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。

计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。

 

 

Semaphore可以被抽象为五个操作:

- 创建 Create

- 等待 Wait:

线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。

-释放 Post

执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程。

-试图等待 TryWait

如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。

-销毁 Destroy

信号量,是可以用来保护两个或多个关键代码段,这些关键代码段不能并发调用。在进入一个关键代码段之前,线程必须获取一个信号量。如果关键代码段中没有任何线程,那么线程会立即进入该框图中的那个部分。一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

动作/系统

Win32

POSIX

创建

CreateSemaphore

sem_init

等待

WaitForSingleObject

sem _wait

释放

ReleaseMutex

sem _post

试图等待

WaitForSingleObject

sem _trywait

销毁

CloseHandle

sem_destroy

 https://blog.csdn.net/cao478208248/article/details/40041211

<think>好的,用户想了解互斥信号量区别。首先,我需要回忆一下操作系统并发控制的相关知识。互斥锁(Mutex)信号量(Semaphore)都是用来管理多线程或多进程之间的同步资源访问的,但它们的用途机制有所不同。 首先,互斥锁的主要作用是保证同一时间只有一个线程可以访问共享资源,防止数据竞争。它通常用于临界区的保护,确保互斥访问。而信号量则更灵活,可以用来控制多个线程对资源的访问,比如限制同时访问的线程数。 接下来,我需要理清它们的核心区别互斥锁是二元状态的,只能锁定或解锁,而信号量可以有一个计数器,允许指定数的线程同时访问。比如,信号量初始化为3的话,可以允许三个线程同时操作资源,而互斥锁一次只能一个。 然后,所有权的问题也很重要。互斥锁具有所有权概念,即哪个线程获取了锁,必须由同一个线程释放。这避免了其他线程误释放锁导致的问题。而信号量没有这种限制,任何线程都可以增减信号量的值。 另外,使用场景不同。互斥锁适合保护临界区,确保一次一个线程执行。信号量适合生产者-消费者问题,或者资源池的管理,比如数据库连接池,限制同时使用的连接数。 可能用户还会混淆这两者的用途,比如用信号量实现互斥锁,虽然技术上可行,但互斥锁更高效,因为它是专门为互斥设计的。同时,互斥锁可能带有优先级继承等高级特性,避免优先级反转问题,而信号量一般不具备这些。 需要注意的是,信号量分为计数信号量二进制信号量,后者功能类似互斥锁,但依然有所有权的区别。例如,二进制信号量可能在释放时不需要由获取者来执行,而互斥锁必须由持有者释放,这可能导致不同的行为。 还需要检查是否有其他区别,比如系统支持情况。互斥锁通常作为线程同步原语,而信号量可能用于进程间同步,不过具体实现可能因系统而异。 最后,总结它们的区别时,要明确互斥锁用于互斥访问,信号量用于控制并发数,以及所有权使用场景的不同。确保回答结构清晰,分点说明,帮助用户逐步理解两者的区别。</think>互斥锁(Mutex)信号量(Semaphore)是并发编程中用于资源同步的两种机制,它们的核心区别体现在设计目的、实现方式应用场景上。以下是逐步分析: --- ### **1. 设计目的不同** - **互斥锁** 用于实现**互斥访问**,确保同一时刻仅有一个线程/进程能访问共享资源。 例如:线程A修改全局变时,锁住资源;线程B必须等待A释放锁后才能操作。 - **信号量** 用于控制**资源访问的并发数**,通过计数器管理多个线程/进程的访问权限。 例如:若信号量初始值为3,则允许最多3个线程同时访问资源。 --- ### **2. 操作机制差异** - **互斥锁** - 二元状态:只有锁定(`Lock`)解锁(`Unlock`)两种状态。 - **所有权概念**:必须由获取锁的线程释放锁,其他线程无法直接释放。 $$ \text{操作流程:Lock() → 访问资源 → Unlock()} $$ - **信号量** - 计数器机制:通过`Wait()`(P操作)`Signal()`(V操作)增减计数器。 - **无所有权限制**:任意线程均可调用`Signal()`释放资源。 $$ \text{初始值} N \geq 0, \quad \text{当} N=0 \text{时阻塞后续线程} $$ --- ### **3. 典型应用场景** - **互斥锁适用场景** - 保护临界区(Critical Section),如修改全局变、写文件等需要严格互斥的操作。 - 示例代码: ```c pthread_mutex_lock(&mutex); // 加锁 // 临界区操作 pthread_mutex_unlock(&mutex); // 解锁 ``` - **信号量适用场景** - 控制资源池(如数据库连接池)、生产者-消费者问题(缓冲队列同步)。 - 示例代码: ```c sem_wait(&sem); // 计数器减1,若计数器为0则阻塞 // 访问资源 sem_post(&sem); // 计数器加1 ``` --- ### **4. 底层实现差异** - **互斥锁** 通常通过硬件支持的原子指令(如CAS,Compare-And-Swap)实现,确保锁操作的原子性。 - **信号量** 依赖操作系统内核的调度机制,`Wait()``Signal()`可能触发上下文切换。 --- ### **5. 关键区别总结** | **特性** | **互斥锁** | **信号量** | |----------------|--------------------------|-------------------------| | 核心功能 | 严格互斥 | 控制并发数 | | 计数器范围 | 0或1(二元) | 任意非负整数(N ≥ 0) | | 所有权 | 必须由持有者释放 | 任意线程可释放 | | 性能开销 | 较低(用户态实现) | 较高(可能涉及内核切换) | | 典型问题防范 | 死锁、优先级反转 | 资源竞争、饥饿 | --- ### **6. 常见误区** - **信号量可以替代互斥锁吗?** 可以(通过初始值为1的信号量),但互斥锁更高效且能避免逻辑错误(如误释放)。 - **互斥锁能控制多线程并发数吗?** 不能。若需允许N个线程并发访问,必须使用信号量。 --- 通过理解上述区别,开发者可以更精准地选择同步机制:优先用互斥锁解决互斥问题,用信号量解决资源配额问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值