7

如果您在应用程序中有两个线程,并且您不希望它们同时运行某段代码,则可以在该段代码周围加一个锁,如下所示:

lock (someObject) {
    // ... some code
}

但是你如何在不同的进程中做同样的事情呢?我认为这就是你使用“全局互斥锁”的目的,所以我Mutex以各种方式尝试了这个类,但它似乎没有满足我的要求,它们是:

  • 如果您是唯一的实例,请继续运行代码。
  • 如果您是第二个实例,请等待第一个实例完成,然后运行代码。
  • 不要抛出异常。

我遇到的问题:

  • 只是Mutex在子句中实例化一个对象using(){...}似乎没有任何作用。这两个实例仍然愉快地同时运行
  • 调用.WaitOne()Mutex 会导致第一个实例运行,第二个实例等待,但第二个实例无限期地等待,即使在第一个调用.ReleaseMutex()并离开using(){}范围之后也是如此。
  • .WaitOne()当第一个进程退出时抛出异常 ( System.Threading.AbandonedMutexException)。

我该如何解决这个问题?不涉及的解决方案Mutex非常受欢迎,特别是因为Mutex它似乎是特定于 Windows 的。

4

6 回答 6

5

我有两个应用程序:

ConsoleApplication1.cs

using System;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Mutex mutex = new Mutex(false, "AwesomeMutex");

            Console.WriteLine("ConsoleApplication1 created mutex, waiting . . .");

            mutex.WaitOne();

            Console.Write("Waiting for input. . .");
            Console.ReadKey(true);

            mutex.ReleaseMutex();
            Console.WriteLine("Disposed mutex");
        }
    }
}

ConsoleApplication2.cs

using System;
using System.Threading;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Mutex mutex = new Mutex(false, "AwesomeMutex");
            Console.WriteLine("ConsoleApplication2 Created mutex");

            mutex.WaitOne();

            Console.WriteLine("ConsoleApplication2 got signalled");

            mutex.ReleaseMutex();
        }
    }
}

启动 ConsoleApplication1,然后是 ConsoleAplication2,可以完美运行,没有错误。如果您的代码仍然失败,那是您的代码存在错误,而不是 Mutex 类。

于 2010-02-24T22:21:34.447 回答
4

我已经成功地将 Mutex 用于此目的,并且可以确认它有效,尽管有一些怪癖。

我在家里有一个完整的工作代码示例。如果您希望我今晚添加代码示例,请对此答案发表评论。

更新:

这是我的生产应用程序中的精简代码。这是一个控制台应用程序,但相同的主体应适用于任何类型的应用程序。使用命令行参数运行

--mutex 

测试互斥逻辑。

请注意,在我的情况下,互斥锁确实保护了大部分过程,但没有理由必须以这种方式使用它。这正是这种情况下所需要的。

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace MyNameSpace
{
    class Program
    {
        // APP_GUID can be any unique string.  I just opted for a Guid.  This isn't my real one :-)
        const string APP_GUID = "1F5D24FA-7032-4A94-DA9B-F2B6240F45AC";

        static int Main(string[] args)
        {
            bool testMutex = false;
            if (args.Length > 0 && args[0].ToUpper() == "--MUTEX")
            {
                testMutex = true;
            }

            // Got variables, now only allow one to run at a time.

            int pid = System.Diagnostics.Process.GetCurrentProcess().Id;

            int rc = 0;

            Mutex mutex = null;
            bool obtainedMutex = false;
            int attempts = 0;
            int MAX_ATTEMPTS = 4;

            try
            {
                mutex = new Mutex(false, "Global\\" + APP_GUID);

                Console.WriteLine("PID " + pid + " request mutex.");

                while (!obtainedMutex && attempts < MAX_ATTEMPTS)
                {
                    try
                    {
                        if (!mutex.WaitOne(2000, false))
                        {
                            Console.WriteLine("PID " + pid + " could not obtain mutex.");
                            // Wait up to 2 seconds to get the mutex
                        }
                        else
                        {
                            obtainedMutex = true;
                        }
                    }
                    catch (AbandonedMutexException)
                    {
                        Console.WriteLine("PID " + pid + " mutex abandoned!");
                        mutex = new Mutex(false, "Global\\" + APP_GUID); // Try to re-create as owner
                    }

                    attempts++;
                }

                if (!obtainedMutex)
                {
                    Console.WriteLine("PID " + pid + " gave up on mutex.");
                    return 102;
                }


                Console.WriteLine("PID " + pid + " got mutex.");

                // This is just to test the mutex... keep one instance open until a key is pressed while
                // other instances attempt to acquire the mutex
                if (testMutex)
                {
                    Console.Write("ENTER to exit mutex test....");
                    Console.ReadKey();
                    return 103;
                }

                // Do useful work here

            }
            finally
            {
                if (mutex != null && obtainedMutex) mutex.ReleaseMutex();
                mutex.Close();
                mutex = null;
            }

            return rc;
        }
    }
}
于 2010-02-24T22:08:38.070 回答
4

您需要使用“命名互斥锁”。有一个单独的构造函数允许您定义一个命名的 mutex

于 2010-02-24T22:08:58.880 回答
0

尽管我建议您使用 Mutex 并调查问题是否出在您的代码本身,但一种非常复杂且高度脆弱的替代方案可能是使用“锁定文件”。

基本上,您在文件系统中创建一个具有两个进程都可以识别的名称的临时文件。如果文件存在,则锁定到位;其他进程应该释放任何其他锁,等待并尝试重新获取。如果文件不存在,则锁未到位;该进程应创建文件以完成锁定并继续处理,释放锁定时删除文件。

正如我所说,它非常复杂和脆弱,但它不使用 Mutex。

或者,使用互斥锁。

于 2010-02-24T22:14:39.240 回答
0

如果您不想使用互斥锁,我会说使用存在的文件来滚动您自己的文件将是一个简单的解决方案。如果文件存在,则不要开始一个新文件。但是,您必须处理进程提前终止且文件未清理的异常情况。

暂时回到互斥锁,这只是一个操作系统“对象”,需要一个更好的术语。在您使用的每个操作系统上,您确实需要这样的东西。我确信其他.net clr 也将允许您访问这些系统原语。我只是使用在每个平台上可能不同的已定义程序集来结束每个程序。

希望这是有道理的。

于 2010-02-24T22:14:54.560 回答
-1

也许我把问题简单化了,但我过去只是检查过进程名称。

您可以只使用此功能等到另一个完成,然后运行当前进程。

''' <summary>
''' Is this app already running as a standalone exe?
''' </summary>
''' <returns></returns>
''' <remarks>
''' NOTE: The .vshost executable runs the whole time Visual Studio is open, not just when debugging.
''' So for now we're only checking if it's running as a standalone.
''' </remarks>
public bool AlreadyRunning()
{
    const string VS_PROCESS_SUFFIX = ".vshost";

    //remove .vshost if present
    string processName = Process.GetCurrentProcess.ProcessName.Replace(VS_PROCESS_SUFFIX, string.Empty);
    
    int standaloneInstances = Process.GetProcessesByName(processName).Length;
    //Dim vsInstances As Integer = Process.GetProcessesByName(processName & VS_PROCESS_SUFFIX).Length
    
    //Return vsInstances + standaloneInstances > 1
    return standaloneInstances > 1;
}
于 2010-02-24T22:31:42.027 回答