7

アプリケーション内に 2 つのスレッドがあり、特定のコードを同時に実行したくない場合は、次のようにコードの周りにロックを設定できます。

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

しかし、別々のプロセスで同じことを行うにはどうすればよいでしょうか? これが「グローバルミューテックス」を使用するものだと思ったのでMutex、さまざまな方法でクラスを試しましたが、次の要件を満たしていないようです。

  • あなたが唯一のインスタンスである場合は、先に進んでコードを実行してください。
  • 2 番目のインスタンスの場合は、最初のインスタンスが完了するまで待ってから、コードを実行します。
  • 例外をスローしないでください。

私が遭遇した問題:

  • Mutex節でオブジェクトをインスタンス化するだけでusing(){...}は何も起こらないようです。2 つのインスタンスは引き続き正常に同時に実行されます
  • Mutex を呼び出す.WaitOne()と、最初のインスタンスが実行され、2 番目のインスタンスが待機しますが、2 番目のインスタンスは、最初の呼び出しがスコープ.ReleaseMutex()を離れた後でも無期限に待機します。using(){}
  • .WaitOne()最初のプロセスが終了したときに例外をスローします ( System.Threading.AbandonedMutexException)。

これを解決するにはどうすればよいですか?特にWindows固有のように見えるため、関与しないソリューションはMutex大歓迎です。Mutex

4

6 に答える 6

5

私には2つのアプリケーションがあります:

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

「名前付きミューテックス」を使用する必要があります。名前付きミューテックスを定義できる別のコンストラクターがあります

于 2010-02-24T22:08:58.880 に答える
0

Mutex を使用して、問題がコード自体にあるかどうかを調査することをお勧めしますが、非常に複雑で非常に壊れやすい代替手段の 1 つは、「ロック ファイル」を使用することです。

基本的に、両方のプロセスが認識できる名前の一時ファイルをファイル システムに作成します。ファイルが存在する場合、ロックが設定されています。他のプロセスは、他のすべてのロックを解放し、待機して、再取得を試みる必要があります。ファイルが存在しない場合、ロックは設定されていません。プロセスは、ロックを完了して処理を続行するためにファイルを作成し、ロックを解放するときにファイルを削除する必要があります。

前述したように、これは非常に複雑で脆弱ですが、Mutex を使用していません。

または、Mutex を使用します。

于 2010-02-24T22:14:39.240 に答える
0

Mutex を使用したくない場合は、たとえば、存在するファイルを使用して独自のロールを作成すると、簡単な解決策になると思います。ファイルが存在する場合は、新しいファイルを開始しないでください。ただし、プロセスが早期に終了し、ファイルがクリーンアップされないという例外ケースを処理する必要があります。

ちょっとミューテックスに戻りますが、これは単に OS の「オブジェクト」であり、より適切な用語が必要です。使用する各 OS には、このようなものが本当に必要です。他の .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 に答える