ChatGPT解决这个技术问题 Extra ChatGPT

什么是互斥锁?

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

什么是互斥体以及如何使用它?

这是一篇关于差异的好文章:barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
互斥锁教程可以帮助您理清思路:stackoverflow.com/questions/4989451/mutex-example-tutorial
互斥锁就像加油站的浴室钥匙,确保一次只能一个人使用浴室,并且在当前居住者用完并归还钥匙之前,其他人不得使用厕所。

i
iwasrobbed

当我在工作中进行激烈的讨论时,我会使用一只橡胶鸡,我将它放在我的办公桌上以备不时之需。拿着鸡的人是唯一可以说话的人。如果你不拿着鸡,你就不能说话。你只能表示你想要鸡肉,等到你拿到鸡后再说话。一旦你说完,你可以把鸡交还给主持人,主持人将把它交给下一个发言的人。这确保了人们不会互相交谈,并且也有自己的交谈空间。

用互斥锁替换鸡,用线程替换人,你基本上就有了互斥锁的概念。

当然,没有橡胶互斥体这样的东西。只有橡胶鸡。我的猫曾经有一只橡胶老鼠,但它们吃了。

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


@SirYakalot,你的意思是鸡是资源,版主是互斥体?
鸡是互斥体。人们抱住亩..鸡是竞争线程。主持人是操作系统。当人们请求鸡肉时,他们会发出锁定请求。当您调用 mutex.lock() 时,您的线程会在 lock() 中停止并向操作系统发出锁定请求。当操作系统检测到互斥锁已从线程中释放时,它只会将其提供给您,然后 lock() 返回 - 互斥锁现在是您的,并且只有您的。没有其他人可以窃取它,因为调用 lock() 会阻止他。还有一个 try_lock() 会在 mutex 是你的时候阻塞并返回 true,如果 mutex 正在使用则立即返回 false。
有时一些编程概念的起源尚不清楚。新手可能会想知道为什么每个人都在谈论正则表达式。 regex 是 [reg]ular [ex]pression 的缩写,这一点并不明显。同样,mutex 是 [mut]ual [ex]clusion 的缩写。这可能会使该术语的含义更容易理解。 @TheSmurf 在他们的答案中链接到它,但出于历史目的将它添加到此处可能会很好。
“当然,没有橡胶互斥体这种东西。”感谢您的澄清。
我在stackoverflow上见过的最好的答案,优雅。
v
vnilla

互斥体是互斥标志。它充当一段代码的看门人,允许一个线程进入并阻止对所有其他线程的访问。这确保了被控制的代码一次只会被一个线程命中。完成后一定要释放互斥锁。 :)


互斥锁与一段代码本身无关,它保护一些资源。如果仅在该代码周围使用互斥锁,则该资源可能是代码段,但是,一旦您开始在代码中的多个位置使用互斥锁,您的解释就会失败。通常它也可以用来保护一些数据结构,这些数据结构可以从代码中的许多地方访问。
@paxdiablo:请允许我不同意。你可以有两段代码,一段使用互斥锁,另一段不使用。您可以从两段代码访问数据结构。互斥锁是如何保护数据结构的?它没有。您将进行数据竞争,就好像根本没有 Mutex 一样。
@Thomas,那么您的代码有错误。说互斥锁不保护数据是因为您没有在需要的地方之一使用互斥锁,这与说您的家庭安全性不足没有什么不同,因为在星期四,您将前门解锁并打开你去上班:-)
@paxdiablo:这就是我的观点:开发人员必须遵循一个约定,即他只使用受 Mutex 保护的代码访问数据结构。互斥锁保护代码。它不保护数据结构。如果我经常开门,我会说我的家庭安全是无用的。然后我以错误的方式实现它,就像我以错误的方式实现代码一样。
@Thomas,我怀疑它只会归结为语义。在我看来,忘记使用互斥锁来保护数据与忘记使用互斥锁来保护代码没有什么不同。您在某些时候忘记保护它,会引起欢闹:-) 我可以理解您的观点,但我必须坚持我的描述,因为运行相同代码的多个线程通常根本没有问题,因为代码本身没有不要在他们下面改变。只有当数据发生意外变化时,他们才会遇到麻烦。
P
Pang

相互排除排除。 Here's the Wikipedia entry on it.

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

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


N
Neurotransmitter

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

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

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

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


C
Community

什么是 Mutex

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

什么是 Race Condition

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

现实生活中的例子:

当我在工作中进行激烈的讨论时,我会使用一只橡胶鸡,我将它放在我的办公桌上以备不时之需。拿着鸡的人是唯一可以说话的人。如果你不拿着鸡,你就不能说话。你只能表示你想要鸡肉,等到你拿到鸡后再说话。一旦你说完,你可以把鸡交还给主持人,主持人将把它交给下一个发言的人。这确保了人们不会互相交谈,并且也有自己的交谈空间。用互斥锁替换鸡,用线程替换人,你基本上就有了互斥锁的概念。 @Xetius

C#中的用法:

此示例显示了如何使用本地 Mutex 对象来同步对受保护资源的访问。因为每个调用线程在获得互斥锁的所有权之前都会被阻塞,所以它必须调用 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 Reference Mutex


互斥锁是如何实现的?它是基于硬件的吗?它是否有一些等待机制来确保所有线程都知道当前状态是什么?
C
Chen A.

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

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

将故事中的厕所转换为共享资源,并将密钥转换为互斥锁。带上厕所的钥匙(获得一把锁)允许你使用它。如果没有钥匙(锁被锁定),您必须等待。当钥匙被人归还(释放锁)时,您现在可以自由地获取它。


但是 c# 示例不支持支持您的队列断言,“将密钥传递给队列中的下一个人”。该示例正在演示堆栈或随机数。 1、2 和 3 都按该顺序请求访问。首先允许一个进入保护区,然后允许三个。一个队列会把它交给第二个。
我不是指任何具体的实现或具体的编程语言。我的示例涉及互斥体的高级抽象作为原则。
R
Radiodef

在 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 语句。


这是一个依赖于实现的答案。此外,在 CS 中,监视器与互斥锁不同。 Monitors 有一个同步机制,但互斥锁只是锁定这个东西,直到不再需要为止。关于实现细节或 C# 语义的 IDK,但我认为问题的上下文更广泛
u
user3751012

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

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

MUTEX 主题 in this pdf file 链接非常值得一读。


“MUTEX 主题”是指信号量部分,因为它的示例是二进制信号量,对吧?
那么 Mutex 只是一个值为 1 的信号量
你分享的那一章的书叫什么名字?请
@OmarFaroqueAnik 引用的书是 CodeSourcery LLC 的 Advanced Linux Programming,由 New Riders Publishing 出版,可以从链接域的主页访问。
首先要了解“分段错误”,您需要知道什么是...
1
18hrs

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


这是真的吗?单个进程不会创建自己的互斥体副本吗?
C
Community

互斥:互斥代表互斥。这意味着在给定时间只有一个进程/线程可以进入临界区。在并发编程中,多个线程/进程更新共享资源(任何变量、共享内存等)可能会导致一些意想不到的结果。 (因为结果取决于哪个线程/进程获得第一次访问)。

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

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。

这确保没有两个进程/线程可以同时访问临界区。