**已关闭。**此问题不符合Stack Overflow guidelines。当前不接受答案。
我们不允许问题寻求有关书籍、工具、软件库等的推荐。你可以编辑问题,以便可以使用事实和引用来回答问题。
两年前关闭了。
Improve this question
我试着去理解互斥锁是如何工作的,我搜索了很多,但仍然留下了一些疑问,因为我创建了自己的程序,在其中锁定不起作用。
互斥锁的一个绝对不直观的语法是pthread_mutex_lock( &mutex1 );
,它看起来像是互斥锁被锁定了,而我真正想锁定的是其他变量。这个语法是否意味着锁定互斥锁会锁定代码区域,直到互斥锁被解锁?那么线程如何知道该区域被锁定了呢?[*UPDATE:线程知道这个区域被 *Memory Fencing锁定了]。这种现象不应该被称为临界区吗?[*UPDATE:临界区对象仅在Windows中可用,在Windows中,临界区对象比互斥锁快,并且仅对实现它的线程可见。否则,临界区仅指受互斥锁保护的代码区域 *]
最简单的互斥量示例程序是什么?关于它如何工作的逻辑,最简单的解释是什么?
7条答案
按热度按时间5sxhfpxr1#
很多人跑到一个单独的电话亭(他们没有移动的)和他们的亲人说话。第一个抓住电话亭门把手的人,是被允许使用电话的人。只要他在使用电话,他就必须一直抓住门把手,否则别人会抓住把手,把他扔出去和他的妻子说话:)没有排队系统。当这个人打完电话,走出电话亭,离开门把手,下一个抓住门把手的人将被允许使用电话。
线程为:每人
互斥锁为:门把手
锁为:人的手
资源为:电话
任何一个线程,如果要执行一些不应该被其他线程同时修改的代码行(比如打电话给妻子),必须首先获得互斥锁(比如抓住电话亭的门把手),只有这样线程才能运行这些代码行(比如打电话)。
一旦线程执行了该代码,它就应该释放互斥锁,以便另一个线程可以获取互斥锁(其他人可以访问电话亭)。
[* 在考虑现实世界的独占访问时,拥有互斥锁的概念有点荒谬,但在编程世界中,我猜没有其他方法可以让其他线程“看到”某个线程已经在执行某些代码行。有递归互斥锁等概念,但这个示例只是为了向您展示基本概念。希望这个示例能让您清楚地了解这个概念。*]
使用C++11线程:
使用
g++ -std=c++0x -pthread -o thread thread.cpp;./thread
编译和运行如果使用作用域锁for the advantage it provides,可以使用括号as shown here,而不是显式地使用
lock
和unlock
。7ajki6be2#
虽然互斥锁可以用来解决其他问题,但它们存在的主要原因是提供互斥,从而解决所谓的争用条件。当两个(或多个)线程或进程试图并发访问同一变量时,就有可能出现争用条件。请考虑以下代码
这个函数的内部结构看起来很简单,只有一条语句。然而,一个典型的伪汇编语言等价物可能是:
因为在i上执行递增操作需要所有等价的汇编语言指令,所以我们说递增i是非原子操作。原子操作是指可以在硬件上完成的操作,并保证一旦指令执行开始就不会被中断。递增i由3个原子指令链组成。在多个线程调用函数的并发系统中,当一个线程在错误的时间读或写时,问题就出现了。假设我们有两个线程同时运行,一个在另一个之后立即调用函数。假设我们将i初始化为0。还假设我们有大量的寄存器,两个线程使用完全不同的寄存器,因此不会有冲突。这些事件的实际时间可能是:
实际情况是,我们有两个线程同时递增i,我们的函数被调用了两次,但结果与事实不符,看起来这个函数只被调用了一次,这是因为原子性在机器级别被“破坏”了,这意味着线程可以在错误的时间相互中断或一起工作。
我们需要一种机制来解决这个问题。我们需要对上面的指令施加一些排序。一种常见的机制是阻塞除一个线程之外的所有线程。Pthread互斥体使用这种机制。
必须执行某些代码行的任何线程,这些代码行可能会同时不安全地修改其他线程的共享值(使用电话与他的妻子交谈),应该首先获得互斥锁上的锁。以这种方式,任何需要访问共享数据的线程都必须通过互斥锁。2只有这样,线程才能执行代码。3这段代码叫做临界区。
一旦线程执行了临界区,它就应该释放互斥锁,以便另一个线程可以获取互斥锁。
考虑到人类寻求对真实的物理对象的独占访问,互斥锁的概念似乎有点奇怪,但在编程时,我们必须是有意的。并发线程和进程没有我们所拥有的社会和文化背景,所以我们必须迫使它们很好地共享数据。
那么从技术上讲,互斥锁是如何工作的呢?它是否也会遇到我们前面提到的竞争条件呢?pthread_mutex_lock()是否比简单的变量增量更复杂一些呢?
从技术上讲,我们需要一些硬件支持来帮助我们。硬件设计师给予我们机器指令,这些指令可以做不止一件事,但保证是原子的。这种指令的一个经典例子是测试和设置当试图获取对资源的锁定时,我们可以使用TAS来检查以查看存储器中的值是否为0。这将是我们的信号,资源正在使用中,我们什么也不做(或者更准确地说,我们通过某种机制等待。一个pthreads互斥体会把我们放进操作系统中的一个特殊队列中,当资源变得可用时会通知我们。Dumber系统可能会要求我们做一个紧密的自旋循环,反复测试条件)。如果存储器中的值不为0,则TAS将位置设置为0以外的值,而不使用任何其他指令。这就像将两个汇编指令组合为1以给予我们原子性。因此,测试和改变值(如果改变是适当的)一旦开始就不能被中断。2我们可以在这样的指令上建立互斥锁。
注:有些部分可能看起来和之前的回答相似,我接受了他的编辑邀请,他更喜欢原来的方式,所以我保留了我的东西,其中注入了一点他的赘言。
aoyhnmkz3#
我最近偶然发现了这篇文章,认为它需要一个针对标准库c++11互斥锁(即std::mutex)的更新解决方案。
我已经粘贴了一些代码如下(我的第一步与互斥锁-我学习了并发在win32与HANDLE,SetEvent,等待多个对象等)。
因为这是我第一次尝试使用std::mutex和friends,我很乐意看到评论、建议和改进!
cpjpxq1n4#
对于那些寻找shortex互斥锁例子的人:
8ehkhllq5#
函数
pthread_mutex_lock()
要么为调用线程 * 获取 * 互斥锁,要么阻塞线程直到获取互斥锁。相关的pthread_mutex_unlock()
释放互斥锁。可以将互斥体看作一个队列;每一个试图获取互斥锁的线程都将被放在队列的末尾。2当一个线程释放互斥锁时,队列中的下一个线程将退出并开始运行。
临界区指的是可能存在非确定性的代码区域。这通常是因为多个线程试图访问一个共享变量。临界区是不安全的,直到某种同步到位。互斥锁是同步的一种形式。
uhry853o6#
在使用互斥锁保护的区域之前,应该检查互斥锁变量,所以pthread_mutex_lock()可以(取决于实现)等到mutex1被释放,或者返回一个值,表明如果其他人已经锁定了锁,则无法获得该锁。
互斥量实际上只是一个简化的信号量。如果你读过关于互斥量的文章,并且理解了它们,你就理解了互斥量。在SO.Difference between binary semaphore and mutex,When should we use mutex and when should we use semaphore等中有几个关于互斥量和信号量的问题。第一个链接中的马桶例子是一个很好的例子。所有代码所做的就是检查密钥是否可用,如果可用,请注意,您实际上并没有保留马桶本身,而是保留了钥匙。
w1e3prcc7#
信号示例:
参考:http://pages.cs.wisc.edu/~remzi/Classes/537/Fall2008/Notes/threads-semaphores.txt