【问题标题】:Are there any loss in this alternative way of using c# Mutex class?这种使用 c# Mutex 类的替代方式有什么损失吗?
【发布时间】:2023-12-27 06:38:01
【问题描述】:

我有一个使用 Mutex 的应用程序(不是我写的),如下所示:

static void Main(string[] args)
{
    Mutex mutex = null;

    try
    {
        mutex = Mutex.OpenExisting("SINGLEINSTANCE");

        if (mutex != null)
        {
            return;
        }
    }
    catch (WaitHandleCannotBeOpenedException ex)
    {
        mutex = new Mutex(true, "SINGLEINSTANCE");
    }

    // critical section here
}

但我知道正确的做法是这样的:

private readonly Mutex m = new Mutex("SINGLEINSTANCE");
static void Main(string[] args) {
    m.WaitOne();
    try {
        /* critical code */
    }
    finally {
        m.ReleaseMutex();
    }
}

这是因为这个应用程序只有一个进程可以同时运行。它是一个控制台应用程序,为 Web 应用程序执行一些异步工作。

这段代码正在生产中,我不想改变它,除非这段代码有什么大问题......有吗?

【问题讨论】:

  • 如果它在生产中并且没有损坏,那么您为什么要更改或修复它?
  • 您的“正确方式”有不同的行为。如果互斥锁存在,旧方法将关闭程序,您的新方法只是等待互斥锁空闲。但是,旧方式仍然存在竞争条件。此外,Mutex 构造函数将 bool 作为参数,该布尔值的 true 或 false 会极大地影响您的代码在此用例中的工作方式。

标签: c# synchronization mutex interprocess


【解决方案1】:

旧代码有一个竞争条件,两个进程可能会尝试同时启动,第二个启动的进程会抛出异常并在new Mutex(true, "SINGLEINSTANCE"); 行崩溃。如果互斥体已经存在并且您没有竞争条件,则程序将正常退出并且永远不会执行临界区。

您的新代码无法编译,因为您没有为Mutex 使用有效的构造函数,但是如果您传入了false,您的代码将等待先前的互斥锁被释放,然后它将继续使用临界区,永远不会提前退出程序。

“正确”的方法是结合这两种方法并尝试创建互斥体,并使用带有out 参数的重载来查看您是否是它的所有者。

static void Main(string[] args)
{
    bool taken;
    using(Mutex mutex = new Mutex(true, "SINGLEINSTANCE", out taken))
    {
        if(!taken)
            return;
        try
        {
            // critical section here
        }
        finally
        {
            mutex.ReleaseMutex();
        }
    }
}

【讨论】: