关于多线程:什么是互斥?

关于多线程:什么是互斥?

What is a mutex?

互斥体是一种经常用于解决多线程问题的编程概念。 我向社区提出的问题:

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


当我在工作中进行激烈的讨论时,我会使用一只橡皮鸡,我会把它留在我的办公桌上。持鸡的人是唯一被允许说话的人。如果你不抓鸡,你就不会说话。你只能表明你想要鸡肉并等到你说话之前就得到它。讲完后,你可以把鸡还给主持人,主持人会把它交给下一个发言的人。这可以确保人们不会互相交谈,也有自己的谈话空间。

用Mutex替换鸡和有线程的人,你基本上有互斥的概念。

当然,没有橡皮互补器这样的东西。只有橡皮鸡。我的猫曾经有过橡皮鼠,但他们吃了它。

当然,在你使用橡皮鸡之前,你需要问问自己,你是否真的需要在一个房间里有5个人,并且一个人在房间做自己的所有工作不仅容易。实际上,这只是扩展了类比,但你明白了。


互斥是一种互斥的旗帜。它充当一段代码的守门员,允许一个线程进入并阻止访问所有其他线程。这可以确保所控制的代码一次只能被一个线程命中。确保在完成后释放互斥锁。 :)


相互排斥。这是维基百科上的条目:

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

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

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


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

这个概念被称为"互斥"(短互斥),并且是一种确保在该区域内只允许一个线程,使用该资源等的方法。

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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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语句。


什么是互斥体?

互斥体(实际上,术语互斥是互斥的缩写)也称为自旋锁是最简单的同步工具,用于保护关键区域,从而防止竞争条件。这是一个线程必须在进入关键部分之前获取锁(在关键部分多线程共享一个公共变量,更新表,写一个文件等),它在离开临界区时释放锁。

什么是比赛条件?

当两个或多个线程可以访问共享数据并且他们尝试同时更改它时,会发生竞争条件。因为线程调度算法可以在任何时间在线程之间交换,所以您不知道线程将尝试访问共享数据的顺序。因此,数据变化的结果取决于线程调度算法,即两个线程都"竞相"访问/改变数据。

真人生活的例子:

When I am having a big heated discussion at work, I use a rubber
chicken which I keep in my desk for just such occasions. The person
holding the chicken is the only person who is allowed to talk. If you
don't hold the chicken you cannot speak. You can only indicate that
you want the chicken and wait until you get it before you speak. Once
you have finished speaking, you can hand the chicken back to the
moderator who will hand it to the next person to speak. This ensures
that people do not speak over each other, and also have their own
space to talk.

Replace Chicken with Mutex and person with thread and you basically have the concept of a mutex.

@Xetius

在C#中的用法:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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参考互斥锁


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

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

这个pdf文件链接中的MUTEX主题非常值得一读。


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

考虑带钥匙的单人厕所。当有人进入时,他们拿钥匙,厕所被占用。如果其他人需要使用厕所,他们需要排队等候。当厕所里的人完成后,他们将钥匙交给排队的下一个人。有道理,对吧?

将故事中的厕所转换为共享资源,以及互斥锁的关键。将钥匙带到马桶上(获得锁)允许您使用它。如果没有钥匙(锁被锁定),你必须等待。当人员返回钥匙(释放锁)时,您现在可以自由获取钥匙。


在需要对多个进程中的资源强制执行独占访问的情况下,互斥锁非常有用,常规锁定无法帮助,因为它只能在线程上运行。


推荐阅读