pthreads互斥与信号量

Posted

技术标签:

【中文标题】pthreads互斥与信号量【英文标题】:pthreads mutex vs semaphore 【发布时间】:2011-01-05 03:55:18 【问题描述】:

pthread库提供的信号量和互斥量有什么区别?

【问题讨论】:

信号量不是由 pthread 提供的,也可以在非线程程序中使用。 任何同步结构都可以在非线程代码中使用:P 嗯,我想强调的区别是信号量在 pthreads 之前使用。您可以将sem_t 放在共享内存中,并使用它来同步进程之间的操作。另一方面,即使您不创建多个线程,也必须使用-pthread 进行编译和链接才能使用pthread_mutex_*。 (有些平台不强制执行此操作,但这是标准。) @ephemient,实际上 Linux 中的 man sem_init 说:Link with -pthread. 所以我想 Linux 并没有完全遵循 POSIX。 【参考方案1】:

信号量有一个同步计数器,互斥量只是二进制(真/假)。

信号量通常用作确定资源的多少元素正在使用的确定机制——例如,代表 n 个工作线程的对象可能使用信号量来计算有多少工作线程可用。

事实上,你可以用一个由互斥锁同步的 INT 来表示一个信号量。

【讨论】:

没有看到opengroup.org/onlinepubs/009695399/functions/sem_wait.html和opengroup.org/onlinepubs/009695399/functions/…。 一个显着的区别(因为我以前见过有人犯过这个错误):信号量可以被任何线程以任何顺序获取和腾出(只要计数永远不会是负数),但是互斥锁可能由锁定它的线程解锁。尝试解锁被另一个线程锁定的互斥锁是未定义的行为。 @ephemient,这将是一个很好的答案,非常有见地 @ephemient:由于您指定的原因,答案中的最后一个断言是错误的:您不能用由互斥锁同步的 INT 表示信号量,因为如果持有互斥锁,您不能从另一个线程增加/减少 int,您必须等待锁定线程释放互斥锁。最基本的区别是互斥体是拥有的,而信号量是不拥有的。而这种所有权,通过强加的同步,被传送给 INT。因此,您获得了一些混合的、拥有的信号量,位于无主信号量和拥有互斥量之间。 我认为这个答案仍然忽略了信号量和互斥量之间的一个非常重要的区别;这就是用法。信号量是其核心的信号机制。它们可以被任何线程递增和递减的事实就是这样的结果。信号量用于向其他控制流发送与同步相关的信号(如完全/空缓冲区)。另一方面,互斥锁始终用于保护对共享对象的多次访问。这是一个很大的区别,人们似乎总是想念它,或者我永远不明白他们想说什么。 :P【参考方案2】:

我将讨论 Mutex 与 Binary-Semaphore。您显然使用互斥锁来防止一个线程中的数据同时被另一个线程访问。

(假设您刚刚调用了 lock() 并正在访问数据。这意味着,您不希望任何其他线程(或相同线程代码的另一个实例)访问相同的数据被同一个互斥锁锁定。也就是说,如果在不同的线程实例上执行相同的线程代码,命中锁,那么 lock() 应该阻塞控制流。)

这适用于使用不同线程代码的线程,该线程也访问相同的数据并且也被相同的互斥锁锁定。

在这种情况下,您仍在访问数据的过程中,您可能需要再花 15 秒才能达到互斥锁解锁(这样在互斥锁中被阻塞的另一个线程将解锁并允许访问数据的控件)。

您是否曾经允许另一个线程解锁同一个互斥锁,然后又允许已经在互斥锁中等待(阻塞)的线程解除阻塞并访问数据? (希望你明白我在说什么。)

根据商定的通用定义,

使用“互斥锁”不会发生这种情况。没有其他线程可以解锁锁 在你的线程中 使用“二进制信号量”可能会发生这种情况。任何其他线程都可以解锁 线程中的锁

因此,如果您非常注重使用二进制信号量而不是互斥锁,那么您应该非常小心地“确定”锁定和解锁的“范围”,我的意思是,每个命中每个锁定的控制流都应该命中解锁调用,也不应该有任何“第一次解锁”,而应该始终是“第一次锁定”。

【讨论】:

我喜欢 scoping 部分。指的是二进制信号量和互斥量不同的实现部分。【参考方案3】:

厕所例子

互斥体:

是厕所的钥匙。一个人可以拥有钥匙——占用厕所——当时。完成后,该人将钥匙交给(释放)队列中的下一个人。

“互斥锁通常用于序列化对不能由多个线程同时执行的可重入代码部分的访问。互斥锁对象仅允许一个线程进入受控部分,从而迫使其他线程尝试获得访问权限到该部分等待直到第一个线程从该部分退出。”

(互斥体实际上是一个值为 1 的信号量。)

信号量:

是免费相同的厕所钥匙的数量。 例如,假设我们有四个带有相同锁和钥匙的厕所。信号量计数 - 键的计数 - 一开始设置为 4(所有四个厕所都是免费的),然后随着人们进来,计数值递减。如果所有厕所都满了,即。没有剩余的空闲键,信号量计数为 0。现在,当 eq.一个人离开厕所,信号量增加到 1(一个空闲键),并提供给队列中的下一个人。

“信号量将共享资源的同时用户数限制为最大数量。线程可以请求对资源的访问(减少信号量),并且可以发出信号表明它们已经完成了对资源的使用(增加信号量) 。”

Source

【讨论】:

【参考方案4】:

mutex 用于避免多个线程之间的竞争条件。

而信号量被用作跨多个进程使用的同步元素。

mutex 不能用二进制信号量替换,因为一个进程等待信号量,而另一个进程释放信号量。如果互斥锁的获取和释放都由同一个处理。

【讨论】:

(-1) 不正确的概括:一方面,互斥锁可以跨进程共享——例如:msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx。如果您的系统没有命名互斥体,只需映射一些共享内存并创建您自己的。 将其标记为“无用”是不公平的。我在 M$ 上工作了很多。告诉某人使用共享内存很容易。在 M$ 上,所有内核对象都被命名和共享。 Mutex 是一个内核对象。 pthread 互斥锁是 M$ 中的 CRITICAL_SECTION。无法在进程之间共享 CRITICAL_SECTION! @hackworks - pthread_mutex 可以用“_POSIX_THREAD_PROCESS_SHARED”标志初始化,允许它在进程间环境中工作:linux.die.net/man/3/pthread_mutexattr_init【参考方案5】:

semaphoremutex 的区别在于机制模式 之间的区别。区别在于它们的目的(意图)和它们的工作方式(行为)。

mutexbarrierpipeline并行编程模式Mutex 用于(意图)保护critical section 并确保mutual exclusionBarrier 使代理(线程/进程)保持相互等待。

mutex 模式的一个特征(行为)是只有允许的代理(进程或线程)才能进入临界区,并且只有代理可以自愿进入没有。

在某些情况下,mutex 一次允许单个代理。在某些情况下,它允许多个代理(多个读取器)并不允许其他一些代理(写入器)。

semaphore 是一个机制,可用于(意图)实现不同的模式。它是(behavior)通常是一个标志(可能受到互斥保护)。 (一个有趣的事实是,甚至mutex 模式也可以用来实现信号量)。

在流行文化中,semaphores 是内核提供的机制,mutexes 是用户空间库提供的。

请注意,对semaphoresmutexes 存在误解。它说semaphores 用于synchronizationmutexesownership。这是由于流行的操作系统书籍。但事实是所有的互斥锁、信号量和屏障都用于同步。互斥的意图不是ownership,而是mutual exclusion。这种误解引发了流行的面试问题,询问mutexesbinary-semaphores 的区别。

总结,

意图 互斥,互斥 信号量,实现并行设计模式 行为 互斥体,只有允许的代理进入临界区,只有它可以退出 信号量,如果 flag 说 go,则输入,否则等到有人更改 flag

从设计的角度来看,mutex 更像state-pattern,其中状态选择的算法可以改变状态。 binary-semaphore 更像strategy-pattern,其中外部算法可以改变状态,最终选择运行的算法/策略。

【讨论】:

【参考方案6】:

这两篇文章详细解释了mutex vs semaphores this 堆栈溢出答案也告诉了类似的答案。

【讨论】:

【参考方案7】:

信号量更多地用作标志,你真的不需要带RTOS / OS。信号量可能会被其他线程意外或故意更改(例如由于编码错误)。 当您线程使用互斥锁时,它拥有资源。在资源释放之前,没有其他线程可以访问它。

【讨论】:

【参考方案8】:

互斥锁只能应用于单个进程中的线程,不能像信号量那样在进程之间工作。

【讨论】:

这是不正确的,见pubs.opengroup.org/onlinepubs/9699919799/functions/…。【参考方案9】:

Mutex 就像 S=1 的信号量。

您可以使用信号量控制并发访问的数量,但使用互斥锁一次只能访问一个进程。

请看下面这两个的实现:(所有函数都是原子的)

信号量:

wait(S) 
      while (S <= 0 )
         ; // busy wait
      S--;


signal(S) 
      S++;

互斥:

acquire() 
      while (!available)
            ; // busy wait
      available = false;


release() 
      available = true;

【讨论】:

以上是关于pthreads互斥与信号量的主要内容,如果未能解决你的问题,请参考以下文章

Linux-线程同步之互斥锁

Linux系统编程-(pthread)线程通信(信号量)

Linux 多线程同步机制:互斥量信号量条件变量

LinuxC线程pthread线程同步进程同步-互斥量信号量条件变量读写锁文件锁

pthreads等待和信号疑问linux

信号处理程序中的 pthread_exit()