二进制信号量和互斥量之间是否有任何区别,或者它们基本相同?
二进制信号量和互斥量之间是否有任何区别,或者它们基本相同?
Answers:
他们不是一回事。它们用于不同目的!
虽然两种类型的信号量都具有完整/空状态并使用相同的API,但它们的用法却大不相同。
互斥信号灯
互斥信号灯用于保护共享资源(数据结构,文件等)。
Mutex信号量由执行该任务的任务“拥有”。如果任务B尝试给任务A当前持有的互斥锁赋值,则任务B的调用将返回错误并失败。
互斥对象始终使用以下顺序:
-SemTake -关键部分 -SemGive
这是一个简单的示例:
线程A线程B 服用Mutex 存取资料 ...以Mutex <==将阻止 ... 授予Mutex访问数据<==解除阻止 ... 给互斥体
Binary Semaphore
Binary Semaphore解决了一个完全不同的问题:
Task A Task B
... Take BinSemaphore <== wait for something
Do Something Noteworthy
Give BinSemaphore do something <== unblocks
请注意,对于二进制信号量,B可以接收信号量,而A可以给出信号量。
同样,二进制信号量不能保护资源免受访问。发出信号量和采取信号量的行为从根本上是分离的。
对于相同的二进制信号量来说,给予和接受相同的任务通常没有多大意义。
因此,信号量更适合于一些同步问题,例如生产者-消费者。
在Windows上,二进制信号量比互斥体更像事件对象。
Mutex can be released only by thread that had acquired it
-我刚刚尝试了一个简单的基于pthread_mutex的程序,一个线程可以解锁锁定在主线程中的互斥锁
厕所的例子很有趣:
互斥体:
是上厕所的钥匙。当时,一个人可以拥有钥匙-上厕所。完成后,此人将密钥释放(释放)给队列中的下一个人。
正式地:“ Mutexe通常用于序列化不能由多个线程同时执行的可重入代码段的访问。互斥对象仅允许一个线程进入受控段,从而迫使其他试图获取访问权限的线程该部分等待,直到第一个线程从该部分退出。” 参考:Symbian开发人员库
(互斥量实际上是值为1的信号量。)
信号:
是免费的相同马桶钥匙的数量。例如,假设我们有四个具有相同锁和钥匙的卫生间。信号量计数(键的计数)在开始时设置为4(所有四个洗手间都是免费的),然后计数值随着人们的进来而递减。如果所有洗手间都已满,即。没有剩余的可用键,信号量为0。一个人离开洗手间,信号量增加到1(一个自由键),并交给队列中的下一个人。
正式地:“信号量将共享资源的并发用户数限制为最大数量。线程可以请求访问该资源(减少信号量),并且可以发出信号,表明他们已经使用完资源(增加信号量)。 ” 参考:Symbian开发人员库
关于该主题的不错的文章:
从第2部分开始:
互斥锁与二进制信号量的原理相似,但有一个明显的区别:所有权原理。所有权是一个简单的概念,当一个任务锁定(获取)一个互斥锁时,它只能解锁(释放)它。如果任务试图解锁一个尚未锁定的互斥锁(因此不拥有),则会遇到错误情况,最重要的是,互斥锁不会解锁。如果互斥对象没有所有权,那么与它所调用的内容无关,它不是互斥体。
由于以上答案都不能消除混乱,因此以下是我的困惑。
严格来说,互斥锁是一种锁定机制用于同步对资源的访问。只有一个任务(可以是基于OS抽象的线程或进程)可以获取互斥量。这意味着将有与互斥锁关联的所有权,只有所有者才能释放锁(互斥锁)。
信号量是信号机制(“我做完了,您可以进行”这种信号)。例如,如果您正在手机上收听歌曲(假设是一项任务),并且您的朋友同时打给您,则将触发一个中断,中断服务程序(ISR)将在此信号通知呼叫处理任务唤醒。
从理论上讲,它们在语义上没有什么不同。您可以使用信号量实现互斥量,反之亦然(请参见此处)的示例)。实际上,实现方式有所不同,它们提供的服务略有不同。
实际的区别(就围绕它们的系统服务而言)是,互斥锁的实现旨在成为一种更轻量级的同步机制。在甲骨文中,互斥锁称为闩锁,信号量称为等待。
在最低级别上,他们使用某种原子测试和设置机制。这将读取内存位置的当前值,计算某种条件的值,并在一条不能中断的指令中写出该位置的值。这意味着您可以获取一个互斥锁并进行测试,以查看是否有人在您之前拥有它。
一个典型的互斥量实现有一个进程或线程执行“测试并设置”指令并评估是否有其他东西设置了互斥量。这里的关键点是,与调度程序之间没有交互,因此我们不知道(也不在乎)谁设置了锁定。然后,我们要么放弃时间片,然后在重新计划任务时重试时间片,要么执行自旋锁。自旋锁是一种类似以下的算法:
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.
在二进制信号量的情况下,两者之间的主要实际区别是围绕实际数据结构的系统服务的性质。
编辑:正如evan正确指出的那样,自旋锁会降低单处理器计算机的速度。您只能在多处理器盒上使用自旋锁,因为在单处理器上,持有互斥锁的进程永远不会在其他任务运行时将其重置。自旋锁仅在多处理器体系结构上有用。
futex
存在Linux 系统调用来辅助低延迟用户空间互斥体/信号量实现。en.wikipedia.org / wiki / Futex)在无争用的快速路径中,或者如果资源很快可用,则您永远不会有开销系统调用。但是您的忙碌等待(旋转)花费的时间不会超过几微秒。当然,调整自旋循环退避和等待的参数取决于硬件和工作负载,但是标准库通常具有合理的选择。
尽管互斥量和信号量被用作同步原语,但它们之间还是有很大的区别。对于互斥锁,只有锁定或获取了互斥锁的线程才能对其进行解锁。对于信号量,等待信号量的线程可以由其他线程发出信号。一些操作系统支持在进程之间使用互斥量和信号量。通常,用法是在共享内存中创建。
Mutex:假设我们有关键部分线程T1要访问它,则它遵循以下步骤。T1:
二进制信号量:它基于信令等待和信号。等待将“ s”值减小一,通常将“ s”值初始化为值“ 1”,信号将“ s”值增大一。如果“ s”值为1表示没有人使用临界区,而值为0则表示正在使用临界区。假设线程T2使用的是关键部分,则遵循以下步骤。T2:
Mutex和Binary信号量之间的主要区别在于Mutext中,如果线程锁定了关键部分,则它必须解锁关键部分,没有其他线程可以解锁它,但是对于Binary信号量,如果一个线程使用wait(s)函数锁定了关键部分,则value的s变为“ 0”,直到“ s”的值变为1之前没有人可以访问它,但假设其他线程调用信号,则“ s”的值变为1,它允许其他函数使用临界区。因此,在二进制信号量线程中没有所有权。
在Windows上,互斥量和二进制信号量之间有两个区别:
互斥锁只能由具有所有权的线程释放,即先前称为Wait函数的线程(或在创建该线程时拥有所有权的线程)。信号量可以由任何线程释放。
线程可以在互斥对象上反复调用wait函数而不会阻塞。但是,如果您在二进制信号量上调用了两次wait函数而没有释放它们之间的信号量,则该线程将阻塞。
显然,您可以使用互斥锁将数据锁定在一个线程中,同时另一个线程可以访问该数据。假设您刚刚打电话lock()
并且正在访问数据。这意味着您不希望任何其他线程(或相同线程代码的另一个实例)访问由相同互斥锁锁定的相同数据。也就是说,如果在不同的线程实例上执行的是相同的线程代码,则将其锁定,然后lock()
应该阻止那里的控制流。这适用于使用不同线程代码的线程,该线程代码也正在访问相同的数据,并且也被相同的互斥锁锁定。在这种情况下,您仍处于访问数据的过程中,并且可能要花15秒钟才能达到互斥锁解锁(这样,在互斥锁中被阻塞的另一个线程将解除阻塞,并使控件能够访问数据)。您是否不惜一切代价允许另一个线程仅解锁同一个互斥锁,然后又允许互斥锁中已在等待(阻塞)的线程解除阻塞并访问数据?希望你明白我在这里说的话吗?根据一致同意的通用定义!,
因此,如果您非常在意使用二进制信号量而不是互斥量,那么在“确定”锁定和解锁范围时应格外小心。我的意思是,碰到每个锁的每个控制流都应该打一个解锁调用,也不应有任何“首次解锁”,而应该始终是“首次锁定”。
互斥锁用于“锁定机制”。一次一个进程可以使用共享资源
而
信号量用于“信号机制”,例如“我已经完成,现在可以继续”
神话:
一对文章说,“二进制信号量和互斥量是相同的”或“值为1的信号量是互斥量”,但基本区别是Mutex只能由已获取它的线程释放,而您可以从任何其他线程发出信号量
关键点:
•一个线程可以获取多个锁(Mutex)。
•互斥锁只能是递归互斥锁才能多次锁定,此处互斥锁的锁定和解锁应该相同
•如果已经锁定了互斥锁的线程再次尝试锁定该互斥锁,它将进入该互斥锁的等待列表,这将导致死锁。
•二进制信号量和互斥量相似但不相同。
•由于与相关的保护协议,Mutex的运行成本很高。
•互斥锁的主要目的是实现原子访问或锁定资源
修改后的问题是-“ Linux”中的互斥锁和“二进制”信号灯有什么区别?
答案:以下是区别– i)范围–互斥锁的范围在创建它的进程地址空间内,并用于线程同步。信号量可以在整个过程空间中使用,因此可以用于进程间同步。
ii)Mutex比信号量轻巧且速度更快。Futex甚至更快。
iii)互斥量可以被同一线程多次成功获取,但前提是它必须释放相同的次数。尝试获取的其他线程将阻塞。而在信号量的情况下,如果相同的过程尝试再次获取它,则会阻塞,因为它只能被获取一次。
Mutex可以阻止关键区域,而Semaphore则可以实现计数。
http://www.geeksforgeeks.org/archives/9102 详细讨论。
Mutex
是用于同步对资源的访问的锁定机制。
Semaphore
是信号机制。
如果他/她想使用二进制信号量代替互斥量,则取决于程序员。
除了互斥对象具有所有者这一事实之外,还可以针对不同的用法优化这两个对象。互斥对象只能保留很短的时间。违反此规定可能会导致性能下降和调度不公。例如,即使另一个线程已被阻塞,正在运行的线程也可能被允许获取互斥量。信号量可以提供更多的公平性,或者可以使用多个条件变量来强制公平性。
sem_post()
的SCHED_FIFO
和SCHED_RR
(这些不是默认):最高优先级的线程,如果有多个具有相同的优先级,已等待时间最长的线程。即使对于正常调度,OpenSolaris在某种程度上也会遵循此FIFO规则。对于glibc和FreeBSD,解锁简单的互斥锁(即不是优先级保护或优先级继承)和发布信号量基本上是相同的,将对象标记为未锁定,然后,如果可能有等待线程,则调用内核唤醒该对象。
虽然可以将二进制信号量用作互斥锁,但是互斥锁是一种更特定的用例,因为只有锁定互斥锁的过程才可以对其进行解锁。此所有权约束使得可以针对以下情况提供保护:
这些约束并不总是存在,因为它们会降低速度。在开发代码期间,您可以临时启用这些检查。
例如,您可以在互斥锁中启用错误检查属性。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");
在浏览完上述帖子后,这个概念对我很清楚。但是还有一些挥之不去的问题。所以,我写了这段小代码。
当我们尝试不使用信号灯时,它就会通过。但是,当您尝试不使用互斥体而给出互斥体时,它将失败。我在Windows平台上对此进行了测试。启用USE_MUTEX以使用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;
}
即使信号量从未拥有该资源,它也可以让您发出信号“使用资源完成操作”这一事实使我认为,在信号量的情况下,拥有和信号之间存在非常松散的耦合。
互斥体
互斥锁通常用于序列化对不能由多个线程同时执行的可重入代码段的访问。互斥对象仅允许一个线程进入受控部分,从而迫使试图访问该部分的其他线程要等到第一个线程退出该部分后再使用。正确使用互斥对象是为了保护共享资源,可能会有危险。意外的副作用。以不同优先级运行并通过互斥体进行协调的任何两个RTOS任务都会为优先级倒置创造机会。Mutex在用户空间中工作。
信号
信号量是一种信号机制。信号量将共享资源的同时用户数限制为最大数量。线程可以请求对资源的访问(减少信号量),并且可以发出信号,表明它们已经完成了对资源的使用(增加信号量)。它允许多个线程访问共享资源。信号量的正确用法是用于将信号从一个任务发送到另一任务,也可以将信号量用于从中断服务程序(ISR)信号发送到任务。发信号量信号是一种无阻塞的RTOS行为,因此具有ISR安全性。由于此技术消除了在任务级别禁用中断的容易出错的需求,因此可以在内核空间中使用。
答案可能取决于目标操作系统。例如,至少一个我熟悉的RTOS实现将允许对单个OS互斥锁进行多个顺序的“获取”操作,只要它们都来自同一线程上下文中即可。在允许另一个线程获取互斥体之前,必须用相等数量的puts替换多个get。 这不同于二进制信号量,二进制信号量一次仅允许一次获取,而与线程上下文无关。
这种互斥量背后的想法是,通过仅允许单个上下文一次修改数据来保护对象。即使线程获取了互斥锁,然后调用了进一步修改对象的函数(并使其自身的操作获取/放置了保护器互斥锁),这些操作仍应是安全的,因为它们都是在单个线程下发生的。
{
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。
最佳解决方案
唯一的区别是
1.互斥锁->锁定和解锁处于锁定互斥锁的线程的所有权之下。
2.Semaphore->没有所有权,即;如果一个线程调用semwait,则其他线程可以调用sempost来删除锁。
MUTEX
直到最近,内核中唯一的睡眠锁是信号灯。大多数使用信号量的用户都实例化了一个计数为1的信号量,并将其视为互斥锁(自旋锁的休眠版本)。不幸的是,信号量是相当通用的,没有施加任何使用限制。这使它们对于在晦涩的情况下(例如内核和用户空间之间的复杂舞蹈)管理独占访问很有用。但这也意味着更难进行锁定,而且缺少强制性规则使得不可能进行任何类型的自动调试或约束性强制执行。为了寻求更简单的睡眠锁,内核开发人员引入了互斥锁。是的,正如您现在所习惯的,这是一个令人困惑的名称。让我们澄清一下。术语“互斥体”是一个通用名称,指的是任何强制实施互斥的睡眠锁,例如使用次数为1的信号量。在最近的Linux内核中,专有名词“互斥锁”现在也是实现互斥的特定类型的睡眠锁。也就是说,互斥锁是互斥锁。
互斥量的简单性和效率来自于信号量所需要的其他限制。与根据Dijkstra的原始设计实现最基本行为的信号量不同,互斥量具有更严格,更狭窄的用例:n一次仅一个任务可以容纳互斥量。也就是说,互斥量的使用次数始终为1。
[1] Linux内核开发,第三版Robert Love
我认为这里的大多数答案令人困惑,尤其是那些说互斥锁只能通过持有互斥锁的进程来释放,而信号量可以通过ay进程来发出信号的人。上面的行在信号量方面有点模糊。要理解,我们应该知道有两种信号量,一种叫做计数信号量,另一种叫做二进制信号量。在计数时,信号量处理对n个资源的访问,其中可以在使用前定义n。每个信号量都有一个count变量,该变量保留使用中的资源数量的计数,最初将其设置为n。每个希望使用资源的进程都对信号量执行wait()操作(从而减少计数)。进程释放资源时,它将执行release()操作(增加计数)。当计数变为0时,所有资源都在使用中。之后,该进程等待,直到计数超过0。这是仅捕获拥有资源的进程可以增加计数的捕获,没有其他进程可以增加计数,只有拥有资源的进程可以增加计数,并且该进程等待信号量再次检查,并在看到可用资源时再次减少计数。因此,就二进制信号量而言,只有持有该信号量的进程才能增加计数,并且计数保持为零,直到它停止使用该信号量并增加计数为止,其他进程才有机会访问该信号量。现在这里是捕获,只有持有资源的进程可以增加计数,没有其他进程可以增加计数,只有持有资源的进程可以增加计数,等待信号量的进程再次检查,并在看到资源可用时再进行计数再次减少计数。因此,就二进制信号量而言,只有持有该信号量的进程才能增加计数,并且计数保持为零,直到它停止使用该信号量并增加计数为止,其他进程才有机会访问该信号量。现在这里是捕获,仅持有资源的进程可以增加计数,而其他进程则不能增加计数,只有持有资源的进程可以增加计数,等待信号量的进程再次检查,并在看到资源可用时再进行计数再次减少计数。因此,就二进制信号量而言,只有持有该信号量的进程才能增加计数,并且计数保持为零,直到它停止使用该信号量并增加计数,其他进程才有机会访问该信号量。
二进制信号量和互斥量之间的主要区别在于,信号量是一种信号传递机制,互斥量是一种锁定机制,但是二进制信号量似乎像互斥量一样起作用,但会造成混淆,但是两者都是适用于不同类型工作的不同概念。