3

オブジェクトの作成中にミューテックスを使用して一部のコードを同期するアプリケーションコードがあります。オブジェクトコンストラクタはミューテックスを取得し、オブジェクトが不要になったときにのみリリースします。したがって、ミューテックスをリリースする場所はオブジェクトデストラクタに1つあります。アプリの2つのインスタンスを使用してコードをデバッグすると、最初のインスタンスが最初にミューテックスを取得し、2番目のインスタンスが座って待機します(mut.WaitOne())。次に、ユーザーは最初のアプリインスタンスを閉じます。この場合、2番目のインスタンスmut.WaitOne()は、「ミューテックスが放棄されたために待機が完了しました」という例外をスローします。これは、最初のインスタンスでmut.ReleaseMutex()が呼び出される前に発生します(MutexReleaseを呼び出す前にオブジェクトデストラクタコードのブレークポイントにヒットしたためです)。ReleaseMutex()が呼び出される前にミューテックスが解放されたため、例外が発生したようです。この競合状態をどのように解決しますか?ご協力ありがとうございました。

public sealed class MyObject
{
    static ExtDeviceDriver devDrv;
    private Mutex mut = new Mutex(false,myMutex);

    public MyObject()
    {
        mut.WaitOne();
        //Thread safe code here.
        devDrv = new ExtDeviceDriver();
    }

    ~MyObject()
    {
        mut.ReleaseMutex();
    }
}
4

2 に答える 2

4

あなたのアプローチには欠陥があります。これは、同期を実行する (しようとする) べき方法ではありません。複数のアプリケーション インスタンスを防止したい場合は、これではなく、それを実行してください。特定の呼び出しを同期する必要がある場合は、可能な限り狭い範囲で同期してください。

元の参照をコピーして別のスレッドで関数呼び出しを行うだけで、アプローチで競合状態を作成できます。コンストラクターを除いて実際には何も同期されません。クラスの複数のインスタンスを作成する以外に何も妨げていません。2 つ目のインスタンスを作成しようとすると、デッドロックが発生します。とても素敵ではありません。

パターンを研究しIDisposableます。ファイナライザーは決定論的ではありません。これは C++ ではなく、デストラクタでもありません。必要なときに実行することに依存することはできません。

次に、ミューテックスは静的でなければなりません。各インスタンスは独自のミューテックスを取得しているため、インスタンス 1 で同期したミューテックスはインスタンス 2 のものとは異なります。これは共有リソースである必要があります。

ドキュメントから:

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

システム全体のミューテックスの場合、放棄されたミューテックスは、アプリケーションが突然終了したことを示している可能性があります (たとえば、Windows タスク マネージャーを使用して)。

そして、ローカルvシステムミューテックスに関してこれを言い続けます...

ミューテックスには、名前のないローカル ミューテックスと名前付きシステム ミューテックスの 2 種類があります。ローカル ミューテックスは、プロセス内にのみ存在します。ミューテックスを表す Mutex オブジェクトへの参照を持つプロセス内の任意のスレッドで使用できます。名前のない各 Mutex オブジェクトは、個別のローカル ミューテックスを表します。

システムミューテックスが必要なようです。どの通話を同期する必要があるかをお知らせください。その方法をご紹介します。以下は非常に基本的な例です。

class Foo
{
    static Mutex _mut(false);
    public MyObject()
    {
        _mut.WaitOne();
        //Thread safe code here.
        devDrv = new ExtDeviceDriver();
        _mut.ReleaseMutex();
    }

    public void SomeSynchronizedMethod()
    {
        // synchronize this call
        _mut.WaitOne();
        devDrv.DoSomething();
        _mut.ReleaseMutex();
    }
}
于 2012-09-21T22:13:43.067 に答える
2

デストラクタは決定論的ではありません。それらがいつ実行されるかについて、CLR による保証はありません。代わりに、クラスに実装し、呼び出し元がそのインスタンスをブロックIDisposableで使用するように強制します。using(...)これにより、必要なときに の実装が確実Disposeに呼び出されます。

例えば、

public sealed class MyObject : IDisposable
{
    static ExtDeviceDriver devDrv;
    private Mutex mut = new Mutex(false,myMutex);

    public MyObject()
    {
        mut.WaitOne();
        //Thread safe code here.
        devDrv = new ExtDeviceDriver();
    }

    public void Dispose() {
        mut.ReleaseMutex();
    }

}

次に、呼び出し元はこれを行う必要があります。

using (var x = new MyObject()) {
    // etc
}

実行フローが存在する場合、例外などに関係なく、usingブロックDisposeが呼び出されます。

于 2012-09-21T22:13:37.047 に答える