什么是互斥锁?


651

互斥锁是一种编程概念,通常用于解决多线程问题。我对社区的问题:

什么是互斥锁,如何使用?


2
这是一篇关于两者之间差异的好文章:barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
Adam Davis

互斥锁的教程可以帮助您解决问题:stackoverflow.com/questions/4989451/mutex-example-tutorial
Nav

1
互斥锁就像加油站的浴室钥匙,可确保一次只有一个人可以使用浴室,并且在当前乘员完成并归还钥匙之前,没有其他人可以使用厕所。
jonschlinkert

Answers:


2147

当我在工作中进行激烈的讨论时,我会使用橡皮鸡,这种情况下我会把它放在桌子上。抱着鸡的人是唯一被允许说话的人。如果不握鸡,就不会说话。您只能指出自己想要这只鸡,然后等到收到它再说。说完之后,您可以将鸡交给主持人,主持人将把鸡交给下一个要发言的人。这样可以确保人们不会互相讲话,也没有自己的交谈空间。

用Mutex替换Chicken并用线程替换person,您基本上就有了互斥锁的概念。

当然,没有橡胶互斥锁。只有橡皮鸡。我的猫曾经有一只橡皮老鼠,但他们吃了它。

当然,在使用橡皮鸡之前,您需要问自己是否在一个房间中实际上需要5个人,而让一个人独自完成所有工作不仅会更轻松吗?实际上,这只是在扩展类比,但您明白了。


9
好答案。是否可以说互斥实际上是阻止鸡通过的规则?鸡就是你要锁的东西吗?
SirYakalot

3
@SirYakalot,您的意思是鸡是资源,主持人是互斥体?
欧文(Owen)2012年

155
鸡是互斥体。人们hoilding万亩..鸡的竞争线程。主持人是操作系统。当人们要求鸡肉时,他们会发出锁定要求。当您调用mutex.lock()时,您的线程将陷入lock()中,并向操作系统发出锁定请求。当操作系统检测到互斥锁已从线程释放时,它仅将其提供给您,然后lock()返回-互斥锁现在是您的,而只有您的。没有其他人可以窃取它,因为调用lock()会阻止他。还有一个try_lock()会在您使用互斥锁时阻止并返回true,如果正在使用互斥锁,则立即返回false。
ПетърПетров

4
你真是个天才 您还能用橡皮鸡的比喻来解释显示器吗?
里卡多2015年

98
有时,某些编程概念的起源尚不清楚。一个新手可能会想知道为什么每个人都在谈论正则表达式。regex并不表示[regular] [expression]。同样,互斥体是[互斥]的缩写。这可能会使术语的含义更易于理解。@TheSmurf在其答案中链接了它,但出于历史目的将其添加到此处可能会很好。
Dodzi Dzakuma 2015年

137

互斥锁是互斥标志。它充当一段代码的网守,允许一个线程进入并阻止对所有其他线程的访问。这样可以确保被控制的代码一次只能被单个线程访问。只要确保完成后释放互斥量即可。:)


10
互斥锁与一段代码本身无关,它可以保护某些资源。如果仅在该代码周围使用互斥锁,则该资源可能是一个代码段,但是,当您开始在代码中的多个位置使用互斥锁的那一刻,您的解释就会失败。通常,它还可以用于保护某些数据结构,可以从代码中的许多位置进行访问。
paxdiablo

72

互斥。这是它上的Wikipedia条目:

http://en.wikipedia.org/wiki/Mutual_exclusion

互斥锁的重点是同步两个线程。当您有两个线程尝试访问单个资源时,通常的模式是在输入代码之前让第一段代码尝试访问以设置互斥量。当第二个代码块尝试访问时,它将看到互斥锁已设置,并等待直到第一个代码块完成(并取消设置互斥锁),然后继续。

如何实现此目标的具体细节显然因编程语言而异。


64

当您具有多线程应用程序时,不同的线程有时会共享一个公共资源,例如变量或类似资源。通常无法同时访问此共享源,因此需要一种构造来确保一次只有一个线程在使用该资源。

该概念称为“互斥”(互斥体的简称),是一种确保使用该资源等在该区域内仅允许一个线程的方式。

如何使用它们是特定于语言的,但是通常(如果不是始终)基于操作系统互斥量。

由于范例,某些语言不需要这种构造,例如函数式编程(Haskell,ML是很好的示例)。


26

在C#中,使用的通用互斥量是Monitor。类型是“ System.Threading.Monitor ”。也可以通过' lock(Object) '语句隐式使用它。使用它的一个例子是在构造Singleton类时。

private static readonly Object instanceLock = new Object();
private static MySingleton instance;
public static MySingleton Instance
{
    lock(instanceLock)
    {
        if(instance == null)
        {
            instance = new MySingleton();
        }
        return instance;
    }
}

使用私有锁对象的lock语句创建一个关键部分。要求每个线程等待,直到前一个线程完成。第一个线程将进入该部分并初始化实例。第二个线程将等待,进入本节,并获取初始化的实例。

静态成员的任何类型的同步都可以类似地使用lock语句。


1
这是与实现有关的答案。另外,在CS中,监视器不同于互斥锁。监视器具有同步机制,但是互斥锁仅锁定事物,直到不再需要为止。有关实现细节或C#语义的IDK,但我认为问题的范围更广
marcoslhc 2015年

23

什么是互斥体

互斥锁(实际上,互斥锁是互斥的简称),也称为自旋锁(spinlock),是最简单的同步工具,用于保护关键区域并因此防止竞争状况。那就是线程在进入临界区之前必须获得锁(在临界区中,多个线程共享一个公共变量,更新表,写入文件等),它在离开临界区时释放该锁。

什么是比赛条件

当两个或多个线程可以访问共享数据并且它们试图同时更改它们时,就会发生竞争状态。因为线程调度算法可以随时在线程之间交换,所以您不知道线程尝试访问共享数据的顺序。因此,数据更改的结果取决于线程调度算法,即两个线程都在“竞争”访问/更改数据。

现实生活中的例子:

当我在工作中进行激烈的讨论时,我会使用橡皮鸡,这种情况下我会把它放在桌子上。抱着鸡的人是唯一被允许说话的人。如果你不抱鸡,你就不会说话。您只能指出自己想要这只鸡,然后等到收到它再说。说完之后,您可以将鸡交给主持人,主持人将把鸡交给下一个要发言的人。这样可以确保人们不会互相讲话,也没有自己的交谈空间。

用Mutex替换Chicken并用线程替换person,您基本上就有了互斥锁的概念。

@Xetius

在C#中的用法:

本示例说明如何使用本地互斥对象来同步对受保护资源的访问。因为每个调用线程在获得互斥体的所有权之前都是被阻塞的,所以必须调用ReleaseMutex方法来释放线程的所有权。

using System;
using System.Threading;

class Example
{
    // Create a new Mutex. The creating thread does not own the mutex.
    private static Mutex mut = new Mutex();
    private const int numIterations = 1;
    private const int numThreads = 3;

    static void Main()
    {
        // Create the threads that will use the protected resource.
        for(int i = 0; i < numThreads; i++)
        {
            Thread newThread = new Thread(new ThreadStart(ThreadProc));
            newThread.Name = String.Format("Thread{0}", i + 1);
            newThread.Start();
        }

        // The main thread exits, but the application continues to
        // run until all foreground threads have exited.
    }

    private static void ThreadProc()
    {
        for(int i = 0; i < numIterations; i++)
        {
            UseResource();
        }
    }

    // This method represents a resource that must be synchronized
    // so that only one thread at a time can enter.
    private static void UseResource()
    {
        // Wait until it is safe to enter.
        Console.WriteLine("{0} is requesting the mutex", 
                          Thread.CurrentThread.Name);
        mut.WaitOne();

        Console.WriteLine("{0} has entered the protected area", 
                          Thread.CurrentThread.Name);

        // Place code to access non-reentrant resources here.

        // Simulate some work.
        Thread.Sleep(500);

        Console.WriteLine("{0} is leaving the protected area", 
            Thread.CurrentThread.Name);

        // Release the Mutex.
        mut.ReleaseMutex();
        Console.WriteLine("{0} has released the mutex", 
            Thread.CurrentThread.Name);
    }
}
// The example displays output like the following:
//       Thread1 is requesting the mutex
//       Thread2 is requesting the mutex
//       Thread1 has entered the protected area
//       Thread3 is requesting the mutex
//       Thread1 is leaving the protected area
//       Thread1 has released the mutex
//       Thread3 has entered the protected area
//       Thread3 is leaving the protected area
//       Thread3 has released the mutex
//       Thread2 has entered the protected area
//       Thread2 is leaving the protected area
//       Thread2 has released the mutex

MSDN参考互斥


1
一个很好的例子
沉思玮申思

21

这里有一些很好的答案,这是另一个很好的类比,可以解释什么是互斥体

考虑带钥匙的单人马桶。当有人进入时,他们拿起钥匙,厕所便被占用。如果有人需要上厕所,他们需要排队等候。当洗完厕所的人洗完澡,他们将钥匙交给下一个排队的人。有道理吧?

将故事中的厕所转换为共享资源,并将密钥转换为互斥体。将钥匙拿到马桶上(获得一把锁)可以使用它。如果没有钥匙(锁已锁定),则必须等待。当人归还钥匙(释放锁)后,您现在就可以自由获取它。


但是c#示例不支持支持队列声明,“将密钥传递给队列中的下一个人”。该示例演示了堆栈或随机数。1、2和3均按该顺序请求访问。首先允许一个进入保护区,然后允许三个。一个队列将把它交给第二个。
donvnielsen

我指的不是任何具体的实现或具体的编程语言。我的示例涉及互斥量的高级抽象原理。
Chen A.

18

首先,要了解MUTEX,您需要知道什么是“比赛条件”,然后才了解为什么需要MUTEX。假设您有一个多线程程序,并且您有两个线程。现在,您在作业队列中有一个作业。第一个线程将检查作业队列,并在找到作业后开始执行它。第二个线程还将检查作业队列,并发现队列中有一个作业。因此,它还将分配相同的作业指针。因此,现在发生了什么,两个线程都在执行相同的作业。这将导致分段错误。这是竞争条件的示例。

这个问题的解决方案是MUTEX。MUTEX是一种锁,一次锁定一个线程。如果另一个线程想要锁定它,则该线程只会被阻塞。

pdf文件链接中的MUTEX主题确实值得阅读。


“ MUTEX主题”指的是信号量部分,因为它的示例是二进制信号量,对吗?
卡尔·G

2
互斥量只是值1的信号量
marcoslhc 2015年

您分享的那章的书名是什么?请
Omar Faroque Anik

@OmarFaroqueAnik所引用的书是CodeSourcery LLC的Advanced Linux Programming,由New Riders Publishing出版,可以从链接域的主页进行访问。
商场

11

互斥体在您需要跨多个进程强制对资源进行独占访问的情况下很有用,在这种情况下,常规锁无济于事,因为它只能跨线程工作。


是真的吗 单个进程不会创建自己的互斥副本吗?
莱昂

0

互斥:互斥代表MUT UAL 防爆 clusion。这意味着一次一个进程/线程可以进入关键部分。在并发编程中,多个线程/进程尝试更新共享资源(任何变量,共享内存等)可能会导致某些意外结果。(结果取决于哪个线程/进程获得第一次访问)。

为了避免这样的意外结果,我们需要某种同步机制,该机制确保一次只有一个线程/进程可以访问这种资源。

pthread库提供对Mutex的支持。

typedef union
{
  struct __pthread_mutex_s
  {
    ***int __lock;***
    unsigned int __count;
    int __owner;
#ifdef __x86_64__
    unsigned int __nusers;
#endif
 int __kind;
#ifdef __x86_64__
    short __spins;
    short __elision;
    __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV      1
# define __PTHREAD_SPINS             0, 0
#else
    unsigned int __nusers;
    __extension__ union
    {
      struct
      {
        short __espins;
        short __elision;
# define __spins __elision_data.__espins
# define __elision __elision_data.__elision
# define __PTHREAD_SPINS         { 0, 0 }
      } __elision_data;
      __pthread_slist_t __list;
    };
#endif

这是互斥量数据类型(即pthread_mutex_t)的结构。互斥锁被锁定时,__lock设置为1。解锁时,__lock设置为0。

这样可以确保没有两个进程/线程可以同时访问关键部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.