代码之家  ›  专栏  ›  技术社区  ›  Timwi

C#:如何防止应用程序的两个实例同时执行相同的操作?

  •  7
  • Timwi  · 技术社区  · 15 年前

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

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

    但是,您如何在不同的流程中执行相同的操作?我认为这就是使用“全局互斥”的目的,所以我尝试了 Mutex 以各种方式初始化,但它似乎不能满足我的要求,即:

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

    我遇到的问题:

    • 只是实例化一个 互斥 对象 using(){...} 子句似乎没有任何作用;这两个实例仍然愉快地同时运行
    • 使命感 .WaitOne() 在互斥锁上,第一个实例运行,第二个实例等待,但第二个实例无限期地等待,即使在第一次调用之后也是如此 .ReleaseMutex() using(){} 范围
    • .WaitOne() 在第一个进程退出时引发异常( System.Threading.AbandonedMutexException ).

    互斥 非常受欢迎,特别是因为 互斥

    6 回复  |  直到 15 年前
        1
  •  5
  •   Juliet    15 年前

    我有两份申请:

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

    控制台应用程序2.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();
            }
        }
    }
    

        2
  •  4
  •   Nick    15 年前
        3
  •  4
  •   Eric J.    15 年前

    我已经成功地将互斥体用于这个目的,并且可以确认它是有效的,尽管有一些怪癖。

    更新:

    --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;
            }
        }
    }
    
        4
  •  0
  •   Randolpho    15 年前

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

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

        5
  •  0
  •   Preet Sangha    15 年前

    如果您不想使用互斥体,我认为使用现有的文件滚动您自己的互斥体将是一个简单的解决方案。如果文件存在,请不要启动新文件。但是,您必须处理进程提前终止且文件未清理的异常情况。

    暂时回到互斥,这只是一个操作系统“对象”,因为缺少更好的术语。在你使用的每一个操作系统上,你真的需要这样的东西。我相信其他.net clr也会允许您访问这些系统原语。我只是使用一个定义的程序集来包装每个平台,在每个平台上可能会有所不同。

    希望这是有意义的。

        6
  •  -1
  •   Adam Neal    15 年前

    也许我过于简化了这个问题,但我只是在过去检查了流程名称。

    您可以使用此函数等待其他进程完成,然后运行当前进程。

    ''' <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;
    }
    
    推荐文章