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

Alb*_*lby 3 c# multithreading mutex

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

已建立新执行个体...

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

实例已获取...

即使我使用以下代码行在500ms后明确释放了互斥锁:

mut.ReleaseMutex();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

PS:有趣的是,我可以通过将false互斥量构造函数作为

static Mutex mut = new Mutex(false, "Global\\test");
Run Code Online (Sandbox Code Playgroud)

这有什么意义initiallyOwned参数的

public Mutex(bool initiallyOwned, string name);
Run Code Online (Sandbox Code Playgroud)

互斥体类的构造函数版本?

从VS运行2个实例的控制台输出

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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Evk*_*Evk 5

因此,要了解一个问题,您需要了解两件事:

  1. initiallyOwned参数如何工作
  2. Mutex重入(递归调用)。

如果Mutex使用initiallyOwned= true 创建-仅在尚未创建此类互斥体的情况下,它将尝试立即获取所有权BUT。因此,应用程序的第一个实例立即获得了互斥锁的所有权。它与执行操作大致相同:

 var mut = new Mutex(false, "Global\\test");
 mut.WaitOne();
Run Code Online (Sandbox Code Playgroud)

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

bool createdNew;
mut = new Mutex(true, "Global\\test", out createdNew);
Run Code Online (Sandbox Code Playgroud)

现在,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢@Evk .. 你已经清楚地解释了我的问题的解决方案。我知道我多次调用 `mut.WaitOne()` 的次数我不得不调用 `mut.ReleaseMutex`,但我是永远不知道`var mut = new Mutex(true, "Global\\test");` 等价于`var mut = new Mutex(false, "Global\\test"); mut.WaitOne();` . 这个解释让我很清楚为什么我无法在我的应用程序的第二个实例中获得互斥锁。 (2认同)