二进制信号量和互斥量之间是否有任何区别,或者它们基本相同?
它们的同步语义非常不同:
因此,可以将互斥锁看作是从一个任务传递到另一个任务的令牌,而将信号量看作是流量红灯( 表示有人可以继续进行操作)。
http://www.geeksforgeeks.org/archives/9102详细讨论。
Mutex
是一种锁定机制,用于同步对资源的访问。 Semaphore
是信号机制。
如果他/她想使用二进制信号量代替互斥量,则取决于程序员。
显然,您可以使用互斥锁将数据锁定在一个线程中,同时另一个线程可以访问该数据。 假设您刚刚调用了lock()
并正在访问数据。 这意味着您不希望任何其他线程(或相同线程代码的另一个实例)访问由相同互斥锁锁定的相同数据。 也就是说,如果在不同的线程实例上执行的是相同的线程代码,则会命中该锁,那么lock()
应该在那里阻塞控制流。 这适用于使用不同线程代码的线程,该线程代码也正在访问相同的数据,并且也被相同的互斥锁锁定。 在这种情况下,您仍处于访问数据的过程中,并且可能要花15秒钟才能达到互斥锁解锁(这样,在互斥锁中被阻塞的另一个线程将解除阻塞,并使控件能够访问数据)。 您是否不惜一切代价允许另一个线程仅解锁同一个互斥锁,然后又允许互斥锁中已在等待(阻塞)的线程取消阻塞并访问数据? 希望你明白我在这里说的话吗? 根据一致同意的通用定义!,
因此,如果您非常在意使用二进制信号量而不是互斥量,那么在“确定”锁定和解锁范围时应格外小心。 我的意思是,碰到每个锁的每个控制流都应该打一个解锁调用,也不应有任何“首次解锁”,而应该始终是“首次锁定”。
关于该主题的不错的文章:
从第2部分开始:
互斥锁与二进制信号量的原理相似,但有一个明显的区别:所有权原理。 所有权是一个简单的概念,当任务锁定(获取)互斥锁时,它只能解锁(释放)它。 如果任务试图解锁一个尚未锁定的互斥锁(因此不拥有),则会遇到错误情况,最重要的是,互斥锁不会解锁。 如果互斥对象不具有所有权,则与其所谓的无关,它不是互斥体。
Mutex用于保护敏感代码和数据,信号量用于同步。您也可以与保护敏感代码一起实际使用,但是可能存在通过操作V释放其他线程的保护的风险。双信号量和互斥量之间的区别是所有权。例如,在洗手间,互斥量就像一个人可以进入马桶并锁上门,直到有人下车,其他人才能进入;双信号量就像一个人可以进入厕所并锁上门,但是其他人可以通过要求管理员打开门来进入,这很荒谬。
互斥锁用于“锁定机制”。 一次一个进程可以使用共享资源
而
信号量用于“信号机制”,例如“我已经完成,现在可以继续”
根据定义, 互斥锁用于序列化对不能由多个线程同时执行的可重入代码段的访问。
根据定义, 信号量将共享资源的同时用户数限制为最大数量
信号量可以是互斥量,但互斥量永远不能是信号量。 这只是意味着二进制信号量可以用作互斥量,但是互斥量永远不能表现出信号量的功能。
如果是Mutex,则拥有Mutex的线程负责释放它。 但是,在信号量的情况下,不需要此条件。 任何其他线程都可以通过使用smps(function.e_ot)发出信号以释放信号量
对开发人员来说另一个重要的区别是,信号量是系统范围的,除非以其他方式进行清理,否则它们将以文件形式保留在文件系统中。 互斥锁是整个进程的,并在进程退出时自动清除。
在浏览完上述帖子后,这个概念对我很清楚。 但是还有一些挥之不去的问题。 所以,我写了这段小代码。
当我们尝试不使用信号灯时,它就会通过。 但是,当您尝试不使用互斥体而给出互斥体时,它将失败。 我在Windows平台上对此进行了测试。 启用USE_MUTEX以使用MUTEX运行相同的代码。
#include
#include
#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;
}
即使信号量从未拥有该资源,它也可以让您发出信号“使用资源完成操作”这一事实使我认为,在信号量的情况下,拥有和信号之间存在非常松散的耦合。
由于以上答案都不能消除混乱,因此以下是我的困惑。
严格来说, 互斥锁是一种锁定机制,用于同步对资源的访问。 只有一个任务(可以是基于OS抽象的线程或进程)可以获取互斥量。 这意味着将有与互斥锁关联的所有权,只有所有者才能释放锁(互斥锁)。
信号量是信号机制 (“我做完了,您可以进行”这种信号)。 例如,如果您正在手机上收听歌曲(假设它是一项任务),并且您的朋友同时打给您,则将触发一个中断,中断服务程序(ISR)将在此信号通知呼叫处理任务唤醒。
资料来源: http : //www.geeksforgeeks.org/mutex-vs-semaphore/
在Windows上,互斥量和二进制信号量之间有两个区别:
互斥锁只能由具有所有权的线程释放,即先前称为Wait函数的线程(或在创建该线程时拥有所有权的线程)。 信号量可以由任何线程释放。
线程可以在互斥对象上反复调用wait函数而不会阻塞。 但是,如果您在二进制信号量上调用了两次wait函数而没有释放它们之间的信号量,则该线程将阻塞。
神话:
几篇文章说“二进制信号量和互斥量相同”或“值为1的信号量是互斥量”,但基本区别是Mutex只能由获取它的线程释放,而您可以从任何其他线程发出信号量
关键点:
•一个线程可以获取多个锁(Mutex)。
•互斥锁只能是递归互斥锁才能多次锁定,此处互斥锁的锁定和解锁应该相同
•如果已经锁定了互斥锁的线程再次尝试锁定该互斥锁,它将进入该互斥锁的等待列表,从而导致死锁。
•二进制信号量和互斥量相似但不相同。
•由于与之相关的保护协议,Mutex的运行成本很高。
•互斥锁的主要目的是实现原子访问或锁定资源
尽管互斥量和信号量被用作同步原语,但它们之间还是有很大的区别。 对于互斥锁,只有锁定或获取了互斥锁的线程才能对其进行解锁。 对于信号量,等待信号量的线程可以由其他线程发出信号。 一些操作系统支持在进程之间使用互斥量和信号量。 通常,用法是在共享内存中创建。
修改后的问题是-A互斥锁与“ Linux”中的“二进制”信号量有什么区别?
答案:以下是区别– i)范围–互斥锁的范围在创建它的进程地址空间内,并用于线程同步。 信号量可以在整个过程空间中使用,因此可以用于进程间同步。
ii)Mutex比信号量轻巧且速度更快。 Futex甚至更快。
iii)互斥量可以被同一线程多次成功获取,但前提是它必须释放相同的次数。 尝试获取的其他线程将阻塞。 而在信号量的情况下,如果相同的过程尝试再次获取它,则它将阻塞,因为它只能被获取一次。
Binary Semaphore和Mutex之间的区别:所有权: 甚至可以从非当前所有者发出信号(张贴)。 这意味着尽管您不是所有者,但您可以从任何其他线程中进行发布。
信号量是一个正在使用的公共财产,可以由非所有者线程简单地发布。 请用粗体字母标记出此差异,这很重要。
Mutex可以阻止关键区域,而Semaphore则可以实现计数。
从理论上讲,它们在语义上没有什么不同。 您可以使用信号量来实现互斥,反之亦然(请参见此处的示例)。 实际上,实现方式有所不同,它们提供的服务略有不同。
实际的区别(就围绕它们的系统服务而言)是,互斥锁的实现旨在成为一种更轻量级的同步机制。 用甲骨文来说,互斥锁称为闩锁 ,信号量称为waits 。
在最低级别上,他们使用某种原子测试和设置机制。 这将读取内存位置的当前值,计算某种条件的值,并在一条不能中断的指令中写出该位置的值。 这意味着您可以获取一个互斥锁并进行测试,以查看是否有人在您之前拥有它。
典型的互斥锁实现有一个进程或线程执行测试设置指令并评估是否有其他任何东西设置了互斥锁。 这里的关键点是与调度程序之间没有交互,因此我们不知道(也不在乎)谁设置了锁定。 然后,我们要么放弃时间片,然后在重新计划任务时重试它,要么执行自旋锁 。 自旋锁是一种类似以下的算法:
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正确指出的那样,自旋锁会降低单处理器计算机的速度。 您只能在多处理器盒上使用自旋锁,因为在单处理器上,持有互斥锁的进程永远不会在其他任务运行时将其重置。 自旋锁仅在多处理器体系结构上有用。
互斥量和二进制信号量的用法相同,但实际上它们是不同的。
如果是互斥锁,只有锁定它的线程才能解锁它。 如果有任何其他线程来锁定它,它将等待。
如果是信号灯,情况并非如此。 信号量不与特定线程ID捆绑在一起。
厕所的例子很有趣:
互斥体:
是上厕所的钥匙。 当时,一个人可以拥有钥匙-上厕所。 完成后,此人将密钥释放(释放)给队列中的下一个人。
正式地:“ Mutexe通常用于序列化不能由多个线程同时执行的可重入代码段的访问。互斥对象仅允许一个线程进入受控段,从而迫使其他试图获取访问权限的线程该部分等待,直到第一个线程从该部分退出。” 参考:Symbian开发人员库
(互斥量实际上是值为1的信号量。)
信号:
是免费的相同马桶钥匙的数量。 例如,假设我们有四个具有相同锁和钥匙的卫生间。 信号量计数(按键计数)在开始时设置为4(所有四个洗手间都是免费的),然后计数值随着人们的进入而递减。如果所有洗手间都已满,即。 没有剩余的可用键,信号量为0。 一个人离开洗手间,信号量增加到1(一个自由键),并交给队列中的下一个人。
正式地:“信号量将共享资源的并发用户数限制为最大数量。线程可以请求访问该资源(减少信号量),并且可以发出信号,表明他们已经使用完资源(增加信号量)。 ” 参考:Symbian开发人员库
除了互斥对象具有所有者这一事实之外,还可以针对不同的用法优化这两个对象。 互斥对象只能保留很短的时间。 违反此规定可能会导致性能下降和调度不公。 例如,即使另一个线程已被阻塞,正在运行的线程也可能被允许获取互斥量。 信号量可以提供更多的公平性,或者可以使用多个条件变量来强制公平性。
正如这里的许多人所提到的,互斥锁用于保护关键代码段(AKA关键段)。您将在同一线程中获取互斥锁(锁定),进入关键段并释放互斥锁(解锁)。
在使用信号量时,您可以让一个线程在信号量(例如线程A)上等待,直到另一个线程(例如线程B)完成任何任务,然后为线程A设置信号量以停止等待并继续其任务。
虽然可以将二进制信号量用作互斥锁,但是互斥锁是一种更特定的用例,因为只有锁定互斥锁的过程才可以对其进行解锁。 此所有权约束使得可以针对以下情况提供保护:
这些约束并不总是存在,因为它们会降低速度。 在开发代码期间,您可以临时启用这些检查。
例如,您可以在互斥锁中启用错误检查属性。 如果尝试两次锁定同一互斥锁,则错误检查互斥锁会返回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");
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,并且允许其他函数使用临界区。 因此,在二进制信号量线程中没有所有权。
互斥锁控制对单个共享资源的访问。 它提供操作来获取()访问该资源并在完成后释放() 。
信号量控制对共享资源池的访问。 它向Wait()提供操作,直到池中的资源之一变为可用为止;向Signal()提供操作时将其返回给池。
当信号量保护的资源数量大于1时,称为计数信号量 。 当它控制一个资源时,它称为Boolean Semaphore 。 布尔信号量等效于互斥量。
因此,信号量是比互斥对象更高级别的抽象。 互斥量可以使用信号量来实现,但不能相反。
“二进制信号量”是一种使用诸如“ mutex”之类的“信号量”的编程语言。 显然有两个很大的不同:
您称呼每个人的方式。
“标识符”的最大长度。
互斥体
互斥锁通常用于序列化对不能由多个线程同时执行的可重入代码段的访问。 互斥对象仅允许一个线程进入受控部分,从而迫使试图访问该部分的其他线程要等到第一个线程退出该部分为止。正确使用互斥对象是为了保护共享资源,可能会有危险。意外的副作用。 以不同优先级运行并通过互斥体进行协调的任何两个RTOS任务都会为优先级倒置创造机会。 Mutex在用户空间中工作 。
信号
信号量是一种信号机制。 信号量将共享资源的同时用户数限制为最大数量。 线程可以请求对资源的访问(减少信号量),并且可以发出信号,表明它们已经完成了对资源的使用(增加信号量)。 它允许多个线程访问共享资源。信号量的正确用法是用于将信号从一个任务发送到另一任务,也可以将信号量用于从中断服务程序 (ISR)信号发送到任务。 发信号量信号是一种无阻塞的RTOS行为,因此具有ISR安全性。 由于此技术消除了在任务级别禁用中断的容易出错的需求,因此可以在内核空间中使用 。
基本问题是并发性。 有不止一个控制流。 考虑使用共享内存的两个进程。 现在,一次只有一个进程可以访问共享内存。 如果一次有多个进程访问共享内存,则共享内存的内容将被破坏。 就像铁轨一样。 只有一列火车可以在其上行驶,否则会发生事故,因此存在一种信号机制,驾驶员可以进行检查。 如果信号为绿色,则火车可以行驶;如果信号为红色,则必须等待使用轨道。 类似地,在共享内存的情况下,有一个二进制信号量。 如果信号量为1,则进程将其获取(将其设置为0)并继续进行访问。 如果信号量为0,则过程等待。 二进制信号量必须提供的功能是互斥(简称互斥),因此许多并发实体(进程或线程)中只有一个相互排斥其他实体。 我们拥有计数信号量的优势,这有助于同步资源的多个实例。
互斥是信号量提供的基本功能。 现在,在线程的上下文中,我们可能有不同的名称和语法。 但是基本概念是相同的:如何在并发编程中保持代码和数据的完整性。 在我看来,所有权和相关检查之类的东西是实现提供的改进。
因此,信号量更适合于一些同步问题,例如生产者-消费者。
在Windows上,二进制信号量比互斥体更像事件对象。
答案可能取决于目标操作系统。 例如,至少一个我熟悉的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。
他们不是一回事。 它们用于不同目的!
虽然两种类型的信号量都具有完整/空状态并使用相同的API,但它们的用法却大不相同。
互斥信号灯
互斥信号量用于保护共享资源(数据结构,文件等)。
Mutex信号量由执行该任务的任务“拥有”。 如果任务B尝试给任务A当前持有的互斥锁赋值,则任务B的调用将返回错误并失败。
互斥对象始终使用以下顺序:
- SemTake - Critical Section - SemGive
这是一个简单的示例:
Thread A Thread B Take Mutex access data ... Take Mutex <== Will block ... Give Mutex access data <== Unblocks ... Give Mutex
二进制信号量
二进制信号量解决了一个完全不同的问题:
Task A Task B
... Take BinSemaphore <== wait for something
Do Something Noteworthy
Give BinSemaphore do something <== unblocks
请注意,对于二进制信号量,B可以接收信号量,而A可以给出信号量。
同样,二进制信号量不能保护资源免受访问。 发出信号量和采取信号量的行为从根本上是分离的。
对于相同的二进制信号量来说,相同的任务通常没有什么意义。
在Windows中,区别如下。 MUTEX:成功执行等待的过程必须执行一个信号 ,反之亦然。 二进制SEMAPHORES:不同的进程可以对信号量执行等待或信号操作。
与信号量不同,互斥体具有所有权。 尽管在互斥锁范围内的任何线程都可以获得解锁的互斥锁并锁定对同一关键代码段的访问,但是只有锁定了互斥锁的线程才应对其进行解锁 。