二进制信号量和互斥量之间有什么区别还是它们本质上是一样的?
36 回答
它们不是一回事。它们用于不同的目的!
虽然两种类型的信号量都具有完整/空状态并使用相同的 API,但它们的用法却大不相同。
互斥信号量
互斥信号量用于保护共享资源(数据结构、文件等)。
Mutex 信号量由接受它的任务“拥有”。如果任务 B 尝试 semGive 任务 A 当前持有的互斥锁,任务 B 的调用将返回错误并失败。
互斥锁总是使用以下顺序:
- SemTake - 关键部分 -SemGive
这是一个简单的例子:
线程 A 线程 B 采取互斥体 访问数据 ... 取 Mutex <== 会阻塞 ... 授予 Mutex 访问数据 <== 解除阻塞 ... 给互斥体
二进制信号量
二进制信号量解决了一个完全不同的问题:
- 任务 B 等待某事发生(例如,传感器被触发)。
- 传感器跳闸和中断服务程序运行。它需要通知任务的行程。
- 任务 B 应运行并为传感器跳闸采取适当的措施。然后回去等待。
Task A Task B
... Take BinSemaphore <== wait for something
Do Something Noteworthy
Give BinSemaphore do something <== unblocks
请注意,对于二进制信号量,B 获取信号量并 A 提供它是可以的。
同样,二进制信号量并不能保护资源不被访问。给予和接受信号量的行为从根本上是分离的。
对于相同的任务来说,对相同的二进制信号量进行给予和接受通常没有什么意义。
- 互斥锁只能由获得它的线程释放。
- 二进制信号量可以由任何线程(或进程)发出信号。
所以信号量更适合一些同步问题,比如生产者-消费者。
在 Windows 上,二进制信号量更像是事件对象而不是互斥体。
厕所的例子是一个有趣的类比:
互斥体:
是厕所的钥匙。一个人可以拥有钥匙——占用厕所——当时。完成后,该人将钥匙交给(释放)队列中的下一个人。
官方:“互斥锁通常用于序列化对不能由多个线程同时执行的可重入代码部分的访问。互斥锁对象只允许一个线程进入受控部分,从而迫使其他线程尝试访问该部分等待直到第一个线程从该部分退出。” 参考:Symbian 开发者库
(互斥锁实际上是一个值为 1 的信号量。)
信号:
是免费相同的厕所钥匙的数量。例如,假设我们有四个带有相同锁和钥匙的厕所。信号量计数 - 键的计数 - 一开始设置为 4(所有四个厕所都是免费的),然后随着人们进来,计数值递减。如果所有厕所都满了,即。没有剩余的空闲键,信号量计数为 0。现在,当 eq. 一个人离开厕所,信号量增加到 1(一个空闲键),并提供给队列中的下一个人。
正式地:“信号量将共享资源的同时用户数限制为最大数量。线程可以请求访问资源(减少信号量),并且可以发出信号表明它们已经完成了对资源的使用(增加信号量)。 " 参考:Symbian 开发者库
关于这个主题的好文章:
从第 2 部分开始:
互斥锁类似于二进制信号量的原理,但有一个显着区别:所有权原理。所有权是一个简单的概念,当任务锁定(获取)互斥体时,只有它可以解锁(释放)它。如果一个任务试图解锁一个尚未锁定(因此不拥有)的互斥锁,则会遇到错误情况,最重要的是,该互斥锁未解锁。如果互斥对象没有所有权,那么与它的名称无关,它不是互斥体。
由于上述答案都没有消除混乱,因此这里有一个消除了我的困惑。
严格来说,互斥锁是一种用于同步对资源的访问的锁定机制。只有一个任务(可以是基于操作系统抽象的线程或进程)可以获得互斥锁。这意味着将有与互斥锁关联的所有权,并且只有所有者才能释放锁(互斥锁)。
信号量是信号机制(“我完成了,你可以继续”的信号)。例如,如果您正在手机上听歌(假设它是一项任务),同时您的朋友打电话给您,则会触发中断,中断服务例程 (ISR) 将向呼叫处理任务发出唤醒信号.
它们的同步语义非常不同:
- 互斥体允许对给定资源的访问序列化,即多个线程一次等待一个锁,如前所述,线程拥有锁直到它完成:只有这个特定的线程可以解锁它。
- 二进制信号量是一个值为 0 和 1 的计数器:一个任务在其上阻塞,直到任何任务执行 sem_post。信号量通告资源可用,并且它提供了等待直到它被通知为可用的机制。
因此,可以将互斥锁视为从任务传递到任务的令牌,将信号量视为交通红灯(它向某人发出信号,表明它可以继续进行)。
在理论上,它们在语义上没有什么不同。您可以使用信号量实现互斥锁,反之亦然(参见此处的示例)。在实践中,实现是不同的,它们提供的服务略有不同。
实际的区别(就围绕它们的系统服务而言)是互斥锁的实现旨在成为一种更轻量级的同步机制。在 oracle-speak 中,互斥锁称为锁存器,信号量称为等待。
在最低级别,他们使用某种原子测试和设置机制。这会读取内存位置的当前值,计算某种条件,并在无法中断的单个指令中写出该位置的值。这意味着您可以获取互斥体并测试以查看是否有其他人在您之前拥有它。
典型的互斥锁实现有一个进程或线程执行测试和设置指令并评估是否有其他任何东西设置了互斥锁。这里的一个关键点是与调度程序没有交互,所以我们不知道(也不关心)谁设置了锁。然后我们要么放弃我们的时间片并在重新安排任务时再次尝试,要么执行自旋锁。自旋锁是一种算法,例如:
Count down from 5000:
i. Execute the test-and-set instruction
ii. If the mutex is clear, we have acquired it in the previous instruction
so we can exit the loop
iii. When we get to zero, give up our time slice.
当我们完成执行受保护的代码(称为临界区)时,我们只需将互斥锁值设置为零或任何意味着“清除”的值。如果多个任务试图获取互斥锁,那么恰好在互斥锁释放后安排的下一个任务将获得对该资源的访问权。通常,您会使用互斥锁来控制同步资源,其中只需要在很短的时间内进行独占访问,通常是为了更新共享数据结构。
信号量是一种同步数据结构(通常使用互斥锁),它具有计数和一些系统调用包装器,它们与调度程序的交互比互斥锁库更深入。信号量递增和递减,用于阻塞任务,直到其他任务准备就绪。有关此问题的简单示例,请参阅生产者/消费者问题。信号量被初始化为某个值——二进制信号量只是信号量初始化为 1 的一种特殊情况。发送到信号量具有唤醒等待进程的效果。
一个基本的信号量算法如下所示:
(somewhere in the program startup)
Initialise the semaphore to its start-up value.
Acquiring a semaphore
i. (synchronised) Attempt to decrement the semaphore value
ii. If the value would be less than zero, put the task on the tail of the list of tasks waiting on the semaphore and give up the time slice.
Posting a semaphore
i. (synchronised) Increment the semaphore value
ii. If the value is greater or equal to the amount requested in the post at the front of the queue, take that task off the queue and make it runnable.
iii. Repeat (ii) for all tasks until the posted value is exhausted or there are no more tasks waiting.
在二进制信号量的情况下,两者之间的主要实际区别在于围绕实际数据结构的系统服务的性质。
编辑:正如埃文正确指出的那样,自旋锁会减慢单处理器机器的速度。您只会在多处理器机器上使用自旋锁,因为在单个处理器上,持有互斥锁的进程永远不会在另一个任务运行时重置它。自旋锁只在多处理器架构上有用。
尽管互斥量和信号量用作同步原语,但它们之间存在很大差异。在互斥锁的情况下,只有锁定或获取互斥锁的线程才能解锁它。在信号量的情况下,等待信号量的线程可以由不同的线程发出信号。一些操作系统支持在进程之间使用互斥量和信号量。通常使用是在共享内存中创建。
互斥锁:假设我们有临界区线程 T1 想要访问它,那么它遵循以下步骤。T1:
- 锁
- 使用临界区
- 开锁
二进制信号量:它基于信号等待和信号工作。等待将“s”值减一通常“s”值初始化为值“1”,信号将“s”值加一。如果 "s" 值为 1 表示没有人在使用临界区,当值为 0 时表示临界区正在使用中。假设线程 T2 正在使用临界区,那么它遵循以下步骤。T2:
- wait(s)//在调用wait之后,最初s的值是1,它的值减1,即0
- 使用临界区
- signal(s) // 现在 s 值增加并变为 1
Mutex 和 Binary semaphore 之间的主要区别在于 Mutext 如果线程锁定了临界区,那么它必须解锁临界区,没有其他线程可以解锁它,但是在 Binary semaphore 的情况下,如果一个线程使用 wait(s) 函数锁定临界区,那么 value s 的值变为“0”,并且在“s”的值变为 1 之前没有人可以访问它,但假设其他一些线程调用信号然后“s”的值变为 1,并且它允许其他函数使用临界区。因此在二进制信号量线程中没有所有权。
在 Windows 上,互斥锁和二进制信号量有两个区别:
互斥锁只能由拥有所有权的线程释放,即先前调用了等待函数的线程(或在创建它时获得所有权的线程)。任何线程都可以释放信号量。
线程可以在互斥体上重复调用等待函数而不会阻塞。但是,如果您在二进制信号量上调用等待函数两次而没有释放其间的信号量,则线程将阻塞。
神话:
几篇文章说“二进制信号量和互斥量相同”或“值为 1 的信号量是互斥量”,但基本区别是互斥量只能由获得它的线程释放,而您可以从任何其他线程发出信号量
关键点:
• 一个线程可以获取多个锁(互斥锁)。
• 一个互斥锁只能在递归互斥锁的情况下被多次锁定,这里互斥锁的锁定和解锁应该相同
• 如果已经锁定互斥锁的线程再次尝试锁定互斥锁,则会进入该互斥锁的等待列表,从而导致死锁。
•二进制信号量和互斥量相似但不相同。
•Mutex 的运行成本很高,因为与之相关的保护协议。
•互斥锁的主要目的是实现原子访问或资源锁定
互斥锁用于“锁定机制”。一次一个进程可以使用共享资源
然而
信号量用于“信号机制”,例如“我完成了,现在可以继续”
您显然使用互斥锁将数据锁定在一个线程中,同时被另一个线程访问。假设您刚刚调用lock()
并在访问数据的过程中。这意味着您不希望任何其他线程(或相同线程代码的另一个实例)访问由相同互斥锁锁定的相同数据。也就是说,如果在不同的线程实例上执行相同的线程代码,命中锁,那么lock()
应该阻止那里的控制流。这适用于使用不同线程代码的线程,该线程也访问相同的数据并且也被相同的互斥锁锁定。在这种情况下,您仍在访问数据的过程中,您可能需要再花 15 秒才能达到互斥锁解锁(这样在互斥锁中被阻塞的另一个线程将解锁并允许控制访问数据)。您是否不惜一切代价允许另一个线程解锁同一个互斥锁,然后允许已经在互斥锁中等待(阻塞)的线程解除阻塞并访问数据?希望你明白我在说什么?根据,商定的通用定义!,
- 使用“互斥锁”,这是不可能发生的。没有其他线程可以解锁您线程中的锁
- 使用“二进制信号量”可能会发生这种情况。任何其他线程都可以解锁您线程中的锁
因此,如果您非常注重使用二进制信号量而不是互斥锁,那么您应该非常小心地“确定”锁定和解锁的范围。我的意思是每个命中每个锁的控制流都应该命中一个解锁调用,也不应该有任何“第一次解锁”,而应该始终是“第一次锁定”。
Mutex控制对单个共享资源的访问。它提供操作来获取()对该资源的访问,并在完成后释放()它。
信号量控制对共享资源池的访问。它向Wait()提供操作,直到池中的资源之一可用,并在将其返回池时向Signal()提供操作。
当 Semaphore 保护的资源数量大于 1 时,称为Counting Semaphore。当它控制一个资源时,它被称为Boolean Semaphore。布尔信号量相当于互斥量。
因此,信号量是比互斥量更高级别的抽象。Mutex 可以使用 Semaphore 来实现,但反之则不行。
Binary Semaphore 和 Mutex 之间的区别: OWNERSHIP: 信号量甚至可以从非当前所有者发出信号(发布)。这意味着您可以简单地从任何其他线程发布,尽管您不是所有者。
信号量是进程中的公共属性,它可以简单地由非所有者线程发布。请用粗体字母标记这种差异,这意味着很多。
修改后的问题是 - “Linux”中的互斥锁和“二进制”信号量有什么区别?
Ans: 以下是不同之处 – i) 范围 – 互斥锁的范围在创建它并用于线程同步的进程地址空间内。而信号量可以跨进程空间使用,因此它可以用于进程间同步。
ii) 互斥量比信号量轻且速度更快。Futex 甚至更快。
iii) Mutex 可以被同一个线程成功多次获取,条件是它应该释放它相同的次数。其他试图获取的线程将阻塞。而在信号量的情况下,如果同一进程尝试再次获取它,它会阻塞,因为它只能获取一次。
Mutex 用于阻塞关键区域,但 Semaphore 用于计数。
http://www.geeksforgeeks.org/archives/9102 详细讨论。
Mutex
是用于同步对资源的访问的锁定机制。
Semaphore
是信号机制。
如果他/她想使用二进制信号量代替互斥量,这取决于程序员。
The concept was clear to me after going over above posts. But there were some lingering questions. So, I wrote this small piece of code.
When we try to give a semaphore without taking it, it goes through. But, when you try to give a mutex without taking it, it fails. I tested this on a Windows platform. Enable USE_MUTEX to run the same code using a MUTEX.
#include <stdio.h>
#include <windows.h>
#define xUSE_MUTEX 1
#define MAX_SEM_COUNT 1
DWORD WINAPI Thread_no_1( LPVOID lpParam );
DWORD WINAPI Thread_no_2( LPVOID lpParam );
HANDLE Handle_Of_Thread_1 = 0;
HANDLE Handle_Of_Thread_2 = 0;
int Data_Of_Thread_1 = 1;
int Data_Of_Thread_2 = 2;
HANDLE ghMutex = NULL;
HANDLE ghSemaphore = NULL;
int main(void)
{
#ifdef USE_MUTEX
ghMutex = CreateMutex( NULL, FALSE, NULL);
if (ghMutex == NULL)
{
printf("CreateMutex error: %d\n", GetLastError());
return 1;
}
#else
// Create a semaphore with initial and max counts of MAX_SEM_COUNT
ghSemaphore = CreateSemaphore(NULL,MAX_SEM_COUNT,MAX_SEM_COUNT,NULL);
if (ghSemaphore == NULL)
{
printf("CreateSemaphore error: %d\n", GetLastError());
return 1;
}
#endif
// Create thread 1.
Handle_Of_Thread_1 = CreateThread( NULL, 0,Thread_no_1, &Data_Of_Thread_1, 0, NULL);
if ( Handle_Of_Thread_1 == NULL)
{
printf("Create first thread problem \n");
return 1;
}
/* sleep for 5 seconds **/
Sleep(5 * 1000);
/*Create thread 2 */
Handle_Of_Thread_2 = CreateThread( NULL, 0,Thread_no_2, &Data_Of_Thread_2, 0, NULL);
if ( Handle_Of_Thread_2 == NULL)
{
printf("Create second thread problem \n");
return 1;
}
// Sleep for 20 seconds
Sleep(20 * 1000);
printf("Out of the program \n");
return 0;
}
int my_critical_section_code(HANDLE thread_handle)
{
#ifdef USE_MUTEX
if(thread_handle == Handle_Of_Thread_1)
{
/* get the lock */
WaitForSingleObject(ghMutex, INFINITE);
printf("Thread 1 holding the mutex \n");
}
#else
/* get the semaphore */
if(thread_handle == Handle_Of_Thread_1)
{
WaitForSingleObject(ghSemaphore, INFINITE);
printf("Thread 1 holding semaphore \n");
}
#endif
if(thread_handle == Handle_Of_Thread_1)
{
/* sleep for 10 seconds */
Sleep(10 * 1000);
#ifdef USE_MUTEX
printf("Thread 1 about to release mutex \n");
#else
printf("Thread 1 about to release semaphore \n");
#endif
}
else
{
/* sleep for 3 secconds */
Sleep(3 * 1000);
}
#ifdef USE_MUTEX
/* release the lock*/
if(!ReleaseMutex(ghMutex))
{
printf("Release Mutex error in thread %d: error # %d\n", (thread_handle == Handle_Of_Thread_1 ? 1:2),GetLastError());
}
#else
if (!ReleaseSemaphore(ghSemaphore,1,NULL) )
{
printf("ReleaseSemaphore error in thread %d: error # %d\n",(thread_handle == Handle_Of_Thread_1 ? 1:2), GetLastError());
}
#endif
return 0;
}
DWORD WINAPI Thread_no_1( LPVOID lpParam )
{
my_critical_section_code(Handle_Of_Thread_1);
return 0;
}
DWORD WINAPI Thread_no_2( LPVOID lpParam )
{
my_critical_section_code(Handle_Of_Thread_2);
return 0;
}
The very fact that semaphore lets you signal "it is done using a resource", even though it never owned the resource, makes me think there is a very loose coupling between owning and signaling in the case of semaphores.
除了互斥体具有所有者这一事实之外,这两个对象还可以针对不同的用途进行优化。互斥锁被设计成只能保持很短的时间;违反这一点可能会导致性能不佳和调度不公平。例如,一个正在运行的线程可能被允许获取一个互斥锁,即使另一个线程已经被阻塞在它上面。信号量可以提供更多的公平性,或者可以使用几个条件变量来强制公平性。
在 Windows 中,区别如下。 MUTEX:成功执行等待的进程必须执行一个信号,反之亦然。二进制信号量:不同的进程可以对一个信号量执行等待或信号操作。
虽然二进制信号量可以用作互斥锁,但互斥锁是一个更具体的用例,因为只有锁定互斥锁的进程才应该解锁它。这种所有权约束可以提供以下保护:
- 意外释放
- 递归死锁
- 任务死亡死锁
这些约束并不总是存在,因为它们会降低速度。在开发代码期间,您可以临时启用这些检查。
例如,您可以在互斥锁中启用错误检查属性。EDEADLK
如果您尝试两次锁定同一个互斥锁并且解锁了不属于您的互斥锁,则会返回错误检查互斥锁EPERM
。
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
pthread_mutex_init (&mutex, &attr);
初始化后,我们可以将这些检查放在我们的代码中,如下所示:
if(pthread_mutex_unlock(&mutex)==EPERM)
printf("Unlock failed:Mutex not owned by this thread\n");
互斥体
互斥锁通常用于序列化对不能由多个线程同时执行的可重入代码部分的访问。互斥对象只允许一个线程进入受控部分,迫使其他试图访问该部分的线程等待,直到第一个线程退出该部分。正确使用互斥锁是为了保护共享资源可能会产生危险意外的副作用。任何两个以不同优先级运行并通过互斥体进行协调的 RTOS 任务都为优先级反转创造了机会。互斥体在用户空间工作。
信号
信号量是一种信号机制。信号量将共享资源的同时用户数限制为最大数量。线程可以请求对资源的访问(减少信号量)并且可以发出信号表明它们已经完成了对资源的使用(增加信号量)。它允许多个线程访问共享资源。信号量的正确使用是从一个任务向另一个任务发出信号。信号量也可用于从中断服务例程(ISR) 向任务发出信号。发送信号量是一种非阻塞 RTOS 行为,因此 ISR 是安全的。因为这种技术消除了在任务级别禁用中断的容易出错的需要。这适用于内核空间。
互斥量用于保护敏感代码和数据,信号量用于同步。保护敏感代码也可以有实际用途,但可能存在通过操作V释放其他线程保护的风险。所以主要bi-semaphore 和 mutex 的区别在于所有权。例如通过厕所,Mutex 就像一个人可以进入厕所并锁门,直到该人出去,其他人才能进入,bi-semaphore 就像一个人可以进入厕所把门锁上了,但让管理员开门就能让别人进来,太可笑了。
最佳解决方案
唯一的区别是
1.Mutex -> lock 和 unlock 属于锁定 mutex 的线程。
2.Semaphore -> 无所有权,即;如果一个线程调用 semwait(s),任何其他线程都可以调用 sempost(s) 来移除锁。
我认为这里的大多数答案都令人困惑,尤其是那些说互斥锁只能由持有它的进程释放但信号量可以由任何进程发出信号的人。就信号量而言,上述行有点模糊。要理解我们应该知道信号量有两种,一种称为计数信号量,另一种称为二进制信号量。计入信号量句柄访问 n 个资源,其中 n 可以在使用前定义。每个信号量都有一个计数变量,它保存正在使用的资源数量的计数,最初,它被设置为 n。每个希望使用资源的进程都会对信号量执行 wait() 操作(从而减少计数)。当进程释放资源时,它会执行 release() 操作(增加计数)。当计数变为 0 时,所有资源都在使用。之后,进程等待直到计数大于 0。现在这里是 catch 只有持有资源的进程可以增加计数,其他进程不能增加计数只有持有资源的进程可以增加计数和进程等待信号量再次检查,当它看到可用资源时,它再次减少计数。所以就二进制信号量而言,只有持有信号量的进程才能增加计数,并且计数保持为零,直到它停止使用信号量并增加计数并且其他进程有机会访问信号量。现在这里是捕获只有持有资源的进程可以增加计数没有其他进程可以增加计数只有持有资源的进程可以增加计数并且等待信号量的进程再次检查并且当它看到可用的资源时再次减少计数。所以就二进制信号量而言,只有持有信号量的进程才能增加计数,并且计数保持为零,直到它停止使用信号量并增加计数并且其他进程有机会访问信号量。现在这里是捕获只有持有资源的进程可以增加计数没有其他进程可以增加计数只有持有资源的进程可以增加计数并且等待信号量的进程再次检查并且当它看到可用的资源时再次减少计数。所以就二进制信号量而言,只有持有信号量的进程才能增加计数,并且计数保持为零,直到它停止使用信号量并增加计数并且其他进程有机会访问信号量。
二进制信号量和互斥量的主要区别在于,信号量是一种信号机制,而互斥量是一种锁定机制,但二进制信号量的功能似乎与互斥量一样会造成混乱,但两者都是不同的概念,适用于不同类型的工作。
答案可能取决于目标操作系统。例如,我熟悉的至少一个 RTOS 实现将允许对单个 OS 互斥体进行多次顺序“获取”操作,只要它们都来自同一个线程上下文。在允许另一个线程获取互斥锁之前,必须将多个获取替换为相等数量的放置。 这与二进制信号量不同,二进制信号量一次只允许一次获取,而与线程上下文无关。
这种类型的互斥体背后的想法是通过一次只允许一个上下文修改数据来保护对象。即使线程获取互斥体,然后调用进一步修改对象的函数(并围绕自己的操作获取/放置保护器互斥体),这些操作仍然应该是安全的,因为它们都发生在单个线程下。
{
mutexGet(); // Other threads can no longer get the mutex.
// Make changes to the protected object.
// ...
objectModify(); // Also gets/puts the mutex. Only allowed from this thread context.
// Make more changes to the protected object.
// ...
mutexPut(); // Finally allows other threads to get the mutex.
}
当然,在使用这个特性时,你必须确定一个线程内的所有访问确实是安全的!
我不确定这种方法有多普遍,或者它是否适用于我熟悉的系统之外。有关此类互斥锁的示例,请参阅 ThreadX RTOS。
与信号量不同,互斥体具有所有权。尽管在互斥锁范围内的任何线程都可以获得解锁的互斥锁并锁定对同一关键代码段的访问,但只有锁定互斥锁的线程才能解锁它。
正如这里的许多人所提到的,互斥锁用于保护关键代码段(AKA 临界区)。您将在同一个线程中获取互斥锁(锁定)、进入临界区和释放互斥锁(解锁) 。
在使用信号量时,您可以让线程等待信号量(例如线程 A),直到另一个线程(例如线程 B)完成任何任务,然后设置线程 A 的信号量以停止等待,并继续其任务。
互斥体
直到最近,内核中唯一的休眠锁是信号量。大多数信号量用户实例化一个计数为 1 的信号量,并将它们视为互斥锁——自旋锁的休眠版本。不幸的是,信号量相当通用,并没有施加任何使用限制。这使得它们对于在不明确的情况下管理独占访问很有用,例如内核和用户空间之间的复杂舞蹈。但这也意味着更简单的锁定更难执行,并且缺乏强制规则使得任何类型的自动调试或约束强制都变得不可能。为了寻求更简单的睡眠锁,内核开发人员引入了互斥锁。是的,正如您现在所习惯的那样,这是一个令人困惑的名字。让我们澄清一下。术语“互斥锁”是一个通用名称,指的是任何强制互斥的休眠锁,例如使用计数为 1 的信号量。在最近的Linux内核中,专有名词“mutex”现在也是一种特定类型的实现互斥的睡眠锁。也就是说,互斥锁就是互斥锁。
互斥体的简单性和效率来自于它强加给用户的额外约束,超出了信号量的要求。与按照 Dijkstra 的原始设计实现最基本行为的信号量不同,互斥锁具有更严格、更窄的用例: n 一次只有一个任务可以持有互斥锁。也就是说,互斥体上的使用计数始终为 1。
- 锁定互斥锁的人必须解锁它。也就是说,您不能在一个上下文中锁定互斥体,然后在另一个上下文中解锁它。这意味着互斥锁不适合内核和用户空间之间更复杂的同步。然而,大多数用例可以从相同的上下文中干净地锁定和解锁。
- 不允许递归锁定和解锁。也就是说,您无法递归获取相同的互斥锁,也无法解锁未锁定的互斥锁。
- 进程在持有互斥锁时无法退出。
- 中断处理程序或下半部分无法获取互斥锁,即使使用 mutex_trylock()。
- 互斥锁只能通过官方 API 进行管理:它必须通过本节中描述的方法进行初始化,并且不能复制、手动初始化或重新初始化。
[1] Linux 内核开发,第三版 Robert Love
互斥量和二进制信号量的用法相同,但实际上它们是不同的。
在互斥锁的情况下,只有锁定它的线程才能解锁它。如果任何其他线程来锁定它,它将等待。
在信号机的情况下,情况并非如此。信号量不与特定的线程 ID 绑定。
基本问题是并发性。控制流不止一种。考虑使用共享内存的两个进程。现在一次只有一个进程可以访问共享内存。如果多个进程一次访问共享内存,共享内存的内容就会损坏。它就像一条铁轨。只有一列火车可以在上面运行,否则会发生事故。所以有一个信号机制,司机检查。如果信号是绿色的,火车可以开行,如果信号是红色的,则必须等待才能使用轨道。类似地,在共享内存的情况下,有一个二进制信号量。如果信号量为 1,则进程获取它(使其为 0)并继续访问它。如果信号量为 0,则进程等待。二进制信号量必须提供的功能是互斥(或互斥,简而言之),以便许多并发实体(进程或线程)中只有一个相互排斥其他实体。我们有计数信号量是一个优点,它有助于同步资源的多个实例。
互斥是信号量提供的基本功能。现在在线程的上下文中,我们可能有不同的名称和语法。但是底层的概念是一样的:如何在并发编程中保持代码和数据的完整性。在我看来,诸如所有权和相关检查之类的东西是实现提供的改进。
互斥量是任何旨在解决关键部分问题的算法都必须遵循的标准(共 4 个),而二进制信号量本身是一个可以取值 0 和 1 的变量。
“二进制信号量”是一种编程语言规避使用诸如«mutex»之类的«semaphore»。显然有两个非常大的区别:
你称呼他们每个人的方式。
“标识符”的最大长度。
您可以通过以下方式清楚地记住差异:
互斥锁:用于保护临界区,互斥锁不能跨进程使用,只能在单个进程中使用
信号量:用于指示资源的可用性。信号量既可以跨进程使用,也可以跨进程使用。
上面几乎所有的人都说对了。让我也试着澄清一下是否有人仍有疑问。
- Mutex -> 用于序列化
- 信号量-> 同步。
两者的目的是不同的,但是,通过仔细编程可以通过它们实现相同的功能。
标准示例-> 生产者消费者问题。
initial value of SemaVar=0
Producer Consumer
--- SemaWait()->decrement SemaVar
produce data
---
SemaSignal SemaVar or SemaVar++ --->consumer unblocks as SemVar is 1 now.
希望我能澄清一下。