web-dev-qa-db-ja.com

C#のミューテックスが取得されていることを確認するにはどうすればよいですか?

C#のミューテックスハンドルからミューテックスが取得されていることを確認するにはどうすればよいですか?

mutex.WaitOne(timeout)がタイムアウトすると、falseを返します。しかし、ミューテックスハンドルからそれをどのように見つけることができますか? (たぶんp/invokeを使用します。)

[〜#〜]更新[〜#〜]

_public class InterProcessLock : IDisposable
{
    readonly Mutex mutex;

    public bool IsAcquired { get; private set; }

    public InterProcessLock(string name, TimeSpan timeout)
    {
        bool created;
        var security = new MutexSecurity();
        security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
        mutex = new Mutex(false, name, out created, security);
        IsAcquired = mutex.WaitOne(timeout);
    }

    #region IDisposable Members

    public void Dispose()
    {
        if (IsAcquired)
        {
            mutex.ReleaseMutex();
            IsAcquired = false;
        }
    }

    #endregion
}
_

現在、ミューテックスを解放する必要があるかどうかを判断するために、自分のプロパティIsAcquiredを使用しています。必須ではありませんが、より明確なのは、IsAcquiredプロパティで表される情報の二次コピーを使用するのではなく、ミューテックスに直接取得するかどうかを尋ねることです。 mutex.ReleaseMutex()を呼び出すと、私が取得しない場合は例外がスローされます。

取得済み状態とは、ミューテックスが所有ミューテックスのときに信号なし状態にあることを意味します。)

(編集: mattdekreyの投稿 のおかげで_IsAcquired = false;_を追加しました。)

22
TN.

ご覧のとおり、Mutexクラスにはパブリックメンバーはありません: http://msdn.Microsoft.com/en-us/library/system.threading.mutex_members.aspx

そのためのパブリックネイティブ関数もありません: http://msdn.Microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx

ただし、特にntdll.dllには、文書化されていない/サポートされていない関数がいくつかあります。これらにより、システムオブジェクトにアクセスできます。ただし、これらの機能は変更されるか、オペレーティングシステムの将来のバージョンで使用できなくなる可能性があります。

したがって、答えは次のとおりです。従来の手段を使用することは不可能です。

6
lioil

これを行うためのクリーンな方法がない理由は、それが良い考えではなく、このタイプのロジックに依存している場合、競合状態が非常に簡単に導入されるためです。したがって、デザインを変更する必要があります。

まず、コンストラクターでロックを取得しないでください。このクラスを、適切に初期化されたミューテックスオブジェクトを返すファクトリに変換します。そうすれば、ロックを取得したかどうかを知ることができます。

ロックを解放するためにDisposeに依存しないでください。これは、維持するのが難しいデッドロックに乗ったコードを要求しています。 try/finallyブロックを使用して、リリースされていることを確認します。

タイムアウトは少し大雑把です。ロックを取得していない場合にのみタイムアウトを使用すると、通常の操作と見なされます。ロックを取得できないことは通常バグであり、タイムアウトでロックを回避するだけでバグが隠されます。タイムアウトが必要な場合は、イベント(AutoResetEventなど)の使用を検討してください。これがより適切な場合があります。

13
Karl Strings

なぜ_Mutex.OpenExisting_を使用できないのですか

_try
{
    Mutex foundMutex = Mutex.OpenExisting("MyTestingMutex");

    // Found Mutex
    foundMutex.ReleaseMutex();
}
catch (System.Threading.WaitHandleCannotBeOpenedException)
{
    //   System.Threading.WaitHandleCannotBeOpenedException:
    //     The named mutex does not exist.
}
_

[〜#〜]編集[〜#〜]

私はこれのいくつかを推測しています。

APIを開発しようとしているようです。 APIで提供しているアイテムの1つは、InterProcessLockです。

スレッド間でコレクションを共有していて、ミューテックスを使用して、一度に1つの操作のみが実行されるようにしていると仮定します。

_using (InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0)))
{
    if(myLock.IsAcquired)
    {
        // I have control then I can delete, add to the collection.
    }
}
_

このデザインを考え直します。 InterProcessLock myLock = new InterProcessLock("LockMutex", TimeSpan.FromMilliseconds(100.0))をusingでラップしたことがない場合はどうなりますか? Disposeは呼び出されません。ユーザーがDisposeをまったく呼び出さなかった場合はどうなりますか?

放棄されたMutexがあります

から [〜#〜] msdn [〜#〜]

注意放棄されたミューテックスは、多くの場合、コードに重大なエラーがあることを示しています。スレッドがミューテックスを解放せずに終了すると、ミューテックスによって保護されているデータ構造が一貫した状態にない可能性があります。ミューテックスの所有権を要求する次のスレッドは、データ構造の整合性を検証できる場合、この例外を処理して続行できます。

ユーザーを保護しようとしている場合は、ユーザーのミューテックスを制御してユーザーを支援し、ユーザーが心配する必要がないようにすることができます。

考えられる例は

_public static bool PerformLockedProcess(Action process, string commonLockName, TimeSpan timeout)
{
    Mutex mutex = null;

    // Get the Mutex for the User
    try
    {
        bool created;
        var security = new MutexSecurity();
        security.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));

        mutex = new Mutex(false, commonLockName, out created, security);

        bool acquired = mutex.WaitOne(timeout);

        if (acquired)
        {
            process();

            return true;
        }

        return false;
    }
    finally
    {
        // Make sure we do not abandon the Mutex
        if (mutex != null)
        {
            try
            {
                mutex.ReleaseMutex();
            }
            catch (ApplicationException)
            {
                // In case that failes
            }
        }
    }
}
_

これは1つの可能な方法です。それはすべて、目標が何であるかに依存します。ミューテックスはオペレーティングシステムの構成要素であるため、最終ユーザーにDisposeを呼び出すように中継しません。また、名前が不明瞭でない場合、同じミューテックス名を使用する他のプロセスに影響を与える可能性があります。

2
David Basarab

まあ、それはあなたが求めているものではありませんが、それはあなたの問題を解決すると思います:Mutexが他の誰かによって取得された場合に発生する例外のために特別にエラー処理を追加しないのはなぜですか?

public void Dispose()
{
    if (IsAcquired)
        try
        { mutex.ReleaseMutex(); }
        catch (System.Threading.SynchronizationLockException)
        {
            // Handle the exception, assuming you need to do anything.
            // All other exceptions would still be passed up the stack.
        }
}
2
Mike U

これは質問の元の投稿者にはメリットがありませんが、ここにあります。

ミューテックスの適切な使用について他のポスターに同意しませんが、自分で所有権を取得せずに誰かがミューテックスを所有しているかどうかをテストする必要があるアプリケーションがありました。他の人が述べたように、唯一の方法は、ntdll.dllからの文書化されていないNtQueryMutantシステムコールを使用することです。次のように使用できるMutexクラスの拡張メソッドを作成しました。

        bool createdNew = true;
        var m = new Mutex(false, MutexName, out createdNew);
        if ( m != null)
        {
            int currentCount;
            bool ownedByCaller, abandonedState;
            if (m.TryQuery(out currentCount, out ownedByCaller, out abandonedState))
            {
                Console.WriteLine(string.Format("Created New: {3}, Count: {0}, OwvedByMe: {1}, Abandoned: {2}",
                    currentCount, ownedByCaller, abandonedState, createdNew));
            }
            m.Close();
        }

そしてここに実装があります

public static class MutexExtensionMethods
{
    public static bool TryQuery(this Mutex m, out int currentCount, out bool ownedByCaller, out bool abandonedState)
    {
        currentCount = -1;
        ownedByCaller = abandonedState = false;
        try
        {
            var handle = m.SafeWaitHandle;
            if (handle != null)
            {
                var h = handle.DangerousGetHandle();
                MutantBasicInformation mbi;
                int retLength;
                var ntStatus = NtQueryMutant(
                    h,
                    MutantInformationClass.MutantBasicInformation,
                    out mbi, 
                    Marshal.SizeOf(typeof(MutantBasicInformation)),
                    out retLength);
                GC.KeepAlive(handle); // Prevent "handle" from being collected before NtQueryMutant returns
                if (ntStatus == 0)
                {
                    currentCount   = mbi.CurrentCount;
                    ownedByCaller  = mbi.OwnedByCaller;
                    abandonedState = mbi.AbandonedState;
                    return true;
                }
            }
        }
        catch
        {
        }
        return false;
    }

    #region NTDLL.DLL

    [DllImport("ntdll.dll")]
    public static extern uint NtQueryMutant(
        [In] IntPtr MutantHandle,
        [In] MutantInformationClass MutantInformationClass,
        [Out] out MutantBasicInformation MutantInformation,
        [In] int MutantInformationLength,
        [Out] [Optional] out int ReturnLength
        );

    public enum MutantInformationClass : int
    {
        MutantBasicInformation
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MutantBasicInformation
    {
        public int CurrentCount;
        [MarshalAs(UnmanagedType.U1)]
        public bool OwnedByCaller;
        [MarshalAs(UnmanagedType.U1)]
        public bool AbandonedState;
    }

    #endregion

}
1
Leo

.NETミューテックスクラスはネイティブミューテックスラッパーであり、ネイティブミューテックスAPIと同じ可能性を提供します(異なるタイプの待機可能なオブジェクトの数を待機することを除く)。ブロックせずにミューテックスを取得したい場合は、mutex.WaitOne(0)を呼び出します。 PInvokeを使用すると、WaitForSingleObjectを呼び出すことができ、同じ結果が得られます。

0
Alex F

名前が示すように、実際にプロセス間ロックを実行しようとしている場合は、ミューテックスが実際に取得されているかどうかを検出する方法が必要になりますよね? InterProcessLockプロパティがない場合、IsAcquiredを使用するコードが確実にロックされるかどうかはわかりません。 (また、誤ってDisposeを2回呼び出すプログラマーから保護するために、IsAcquiredメソッドでfalseDisposeに設定します。)

私は自分で同じことを実装しました(試してみるよりもブロックを使用する方がはるかに好きです-最終的にはミューテックスを解放するためだけです)代わりに、タイムアウトを超えたときに例外をスローしました。プロジェクトを正しく覚えていれば、 Disposeメソッドを呼び出さないでください。

編集: コンストラクターで例外をスローするという追加の利点:クリティカルセクションも完全に回避され、catchブロックでエラー処理を実行できます。これには、とにかく、クリティカルセクションが持っていたのと同じメソッド呼び出しを含めることができます。個人的にはそれは悪い習慣だと思います。

さらに熟考すると、別の回答で指定されているように_try ... catch_を使用するのではなく、処分に次のものを使用できます。

_public void Dispose()
{
    if (IsAcquired)
    {
        lock (mutex) 
        {
            mutex.ReleaseMutex();
            IsAcquired = false;
        }
    }
}
_

ミューテックスをlockするのは少し皮肉な感じがしますが、それはあります。 IDisposableインターフェイスのドキュメントがあるため、Disposeの呼び出しに依存するべきではないことに完全に同意しますが、using() { }ブロックで示されるプロセス間クリティカルセクションがあると非常に便利だと思います。

0
Matt DeKrey