从 Visual Studio 启动时,C# ReleaseMutex() 未在控制台应用程序的多个实例中释放互斥锁

Posted

技术标签:

【中文标题】从 Visual Studio 启动时,C# ReleaseMutex() 未在控制台应用程序的多个实例中释放互斥锁【英文标题】:C# ReleaseMutex() is not releasing mutex in multiple instances of console application while launching from Visual Studio 【发布时间】:2017-02-16 13:30:58 【问题描述】:

我编写了一个示例控制台应用程序,它创建了一个互斥锁,如下面的代码示例所示。我通过按 Ctrl + F5 (在没有调试器的情况下运行应用程序)直接从 Visual Studio (VS2013) 启动此应用程序。对于控制台应用程序的第一个实例,我获取了互斥锁,并且控制台中显示了以下行:

已创建新实例...

但是,当我再次使用 Ctrl + F5 创建控制台应用程序的第二个实例时,我在控制台中收到以下消息:

实例已经获得...

即使我使用这行代码在 500 毫秒后显式释放互斥锁:

mut.ReleaseMutex();

在获取互斥锁的同一个线程中,我仍然看到我的第二个控制台应用程序实例等待互斥锁被释放。

如果我做错了什么,有人可以向我解释为什么会这样或纠正我吗?如果我理解,ReleaseMutex 应该从通过mut.WaitOne(0) 调用获取互斥锁的同一线程释放互斥锁,以便为任何其他等待获取互斥锁的线程提供所有权。但是,在这种情况下,我无法看到它工作。

如果我关闭获得互斥锁的第一个实例(我的第二个仍然存在)并尝试使用 Ctrl+F5 启动第三个实例,我可以看到有一个 AbandonedMutexException:

未处理的异常:System.Threading.AbandonedMutexException:由于被放弃的互斥体,等待已完成。

PS:有趣的是,如果我在 mutex 构造函数中将 false 传递为

static Mutex mut = new Mutex(false, "Global\\test");

initiallyOwned参数中的意义是什么

public Mutex(bool initiallyOwned, string name);

互斥类的构造函数版本?

Console output for 2 instances run from VS

class Program

    static Mutex mut = new Mutex(true, "Global\\test");

    static void Main(string[] args)
    

        if (IsInstance())
        
            Console.WriteLine("New Instance created...");
        
        else
        
            Console.WriteLine("Instance already acquired...");
        

        Console.ReadLine();
    
    static bool IsInstance()
    
        if (!mut.WaitOne(0))
        
            Console.WriteLine("Thread id 0 Waiting at Mutex...",AppDomain.GetCurrentThreadId());
            return false;
        
        else
        
           Console.WriteLine("Thread id 0 got Mutex...", AppDomain.GetCurrentThreadId());
           Thread.Sleep(500);
           mut.ReleaseMutex();
           return true;
        
    


【问题讨论】:

如果将true作为initiallyOwned传递,当前线程将在创建后立即尝试获取互斥锁。 @Leandro Taset 是的,我明白这一点,但为什么同一线程上的调用mut.ReleaseMutex(); 没有释放互斥锁?这就是我想要理解的。有趣的是看到false 可以正常工作,为什么不使用true。如果它打算这样工作,在哪些情况下应该使用true,以及在哪些情况下使用false 正如@LeandroTaset 所说,这种模式被破坏了,因为它导致了废弃的互斥锁。请参阅this 答案。 “它本来就是这样工作的” - 不,如果你运行你的第一个副本,它将占用互斥锁(由于构造函数参数),但会失败WaitOne 检查 = 不释放 = 放弃互斥锁。 WaitOne 不是“检查此线程是否拥有所有权”,而是“尝试获取所有权”,因此它将返回 false(因为 something 已经拥有互斥锁)。阅读有关abandoned mutex 的更多信息。 @Evk,感谢您的解释。我现在已经澄清了我的疑问.. 【参考方案1】:

所以要理解一个问题,你需要知道两件事:

    initiallyOwned 参数的工作原理 互斥重入(递归调用)。

如果您使用initiallyOwned = true 创建Mutex - 它将尝试立即获取所有权,但只有在尚未创建此类互斥体时。因此,您的应用程序的第一个实例会立即获得互斥锁的所有权。这和做的差不多:

 var mut = new Mutex(false, "Global\\test");
 mut.WaitOne();

如果这个互斥体已经存在,它不会尝试获取所有权。要查看是否创建了互斥锁(因此,您已经拥有它),您可以使用此重载:

bool createdNew;
mut = new Mutex(true, "Global\\test", out createdNew);

现在,Mutex 允许从同一线程多次调用 WaitOneReleaseMutex。如果您多次调用WaitOne - 您需要调用ReleaseMutex 相同的次数才能释放它。但是,对于您的第一个实例,您调用 WaitOne 两次:第一次是因为 initiallyOwned 参数(并且因为互斥锁尚不存在并且已创建),第二次您显式调用它。但是您只调用一次ReleaseMutex,因此您的互斥锁不会被释放并且由第一个实例拥有。当您关闭此实例时 - 互斥锁仍未释放,因此被放弃。

以下是说明这些要点的代码示例:

static Mutex mut;

static void Main(string[] args)

    bool createdNew;
    mut = new Mutex(true, "Global\\test", out createdNew);
    if (createdNew) 
        Console.WriteLine("New instance created with initially owned = true");
    
    else if (IsInstance())
    
        Console.WriteLine("New Instance created...");
    
    else
    
        Console.WriteLine("Instance already acquired...");
    
    if (createdNew)
    
        Thread.Sleep(500);
        mut.ReleaseMutex();
    
    Console.ReadLine();


static bool IsInstance()

    if (!mut.WaitOne(0))
    
        Console.WriteLine("Thread id 0 Waiting at Mutex...", AppDomain.GetCurrentThreadId());
        return false;
    
    else
    
        Console.WriteLine("Thread id 0 got Mutex...", AppDomain.GetCurrentThreadId());
        Thread.Sleep(500);
        mut.ReleaseMutex();            
        return true;
    

【讨论】:

谢谢@Evk .. 你已经清楚地解释了我的问题的解决方案。我知道我打电话给mut.WaitOne() 的次数是我必须打电话给mut.ReleaseMutex 的次数,但是我从来不知道 var mut = new Mutex(true, "Global\\test"); 等同于 var mut = new Mutex(false, "Global\\test"); mut.WaitOne(); 。这个解释让我明白了为什么我无法在我的应用程序的第二个实例中获取 Mutex。

以上是关于从 Visual Studio 启动时,C# ReleaseMutex() 未在控制台应用程序的多个实例中释放互斥锁的主要内容,如果未能解决你的问题,请参考以下文章

C# Visual Studio 2010 未开始调试

如何在 Win 7 Visual Studio 2012 的 C# 中禁用#if DEBUG

C# / C++ 应用程序在从 Windows 运行时崩溃,而不是从 Visual Studio 运行

Visual Studio 2010 C# 调试器不会在新断点处停止

Visual Studio 分析:从代码开始/停止

Visual Studio 在启动时崩溃