4

私の MVC 3 C# アプリケーションには、一度に 1 つの要求で使用できるようにしたい静的オブジェクトがあります。それへのアクセスはメソッドを介してのみですが、そのメソッドを呼び出す間、ロックを保持したいと思います。

呼び出しはコントローラーでのみ行われ、通常は 1 つまたは 2 つのロックされたコード ブロックがあります。

最初は、いくつかの静的パブリック オブジェクトを公開し、それを次のように単純に使用したいと考えていました。

lock(MyClass.lockObject)
{
 MyClass.doStuff();
 MyClass.doStuff2();
}

、しかし、どこかにロックするのを忘れる可能性があるため、エラーが発生しやすいと思います。Monitor.Enter()コンストラクターとMonitor.Exit()Dispose メソッドで使用してから、メソッドを非静的に変更するのが適切な方法なのだろうか? 次のように言います。

public class MyClass:IDisposable
{
    static protected object _locker = new object();
    protected bool isDisposed = false;

    public MyClass()
    {
        Monitor.Enter(_locker);
    }

    public void Dispose()
    {
        if (!isDisposed)
        {
            Monitor.Exit(_locker);
            GC.SuppressFinalize(this);
            isDisposed = true;
        }
    }

    ~SPInstances()
    {
        Dispose();
    }

    public void doStuff()
    {
        if(isDisposed) throw new ObjectDisposedException();
        // do stuff here, etc.
    }
}

次に、次のように使用できます。

using(MyClass myinstance = new MyClass())
{
    myInstance.doStuff();
    myInstance.doStuff2();
}

次に、使用中にコードをラップするのを忘れたとしても、ロックされ、ガベージコレクション中にロックが解除される可能性があります...

私は C# に精通しておらず、いくつかの側面を見落とすこともあります。また、スレッド化は後でデバッグするのが容易ではないため、順調に進んでいるかどうか疑問に思っています。それは私の目標を達成するための適切な方法ですか?

編集:

Master Morality のアイデアを拡張しますが、この方法の方がよいでしょうか (リソースのインスタンスが 1 つしか必要ないため、少し単純化しました)?

public class MyClass
{
    static protected readonly MyResourceType _myResourceStatic = new MyResourceType();
    static public void DoWork(Action<MyClass> action)
    {
        lock(_myResource)
        {
            action(new MyClass(_myResource));
        }        
    }

    protected MyClass(MyResourceType myResource)
    {
        _myResource = myResource;
    }
    protected readonly _myResource;

    public void DoFirstThing() { ... }
    public void DoSecondThing(){ ... }
}

MyClass.DoWork(x => 
{
    x.DoFirstThing();
    // do unrelated stuff
    x.DoSecondThing();
});
4

5 に答える 5

2

私見はlockあなた自身の方法の中にある方が良いです。そうすれば、別のプログラマー、または後で自分自身がlock、メソッドを呼び出す前に覚えておく必要がなく、十分に単純です。

public class MyClass
{
    private static readonly object _gate = new object();

    /* something that can only be accessed by one thread at a time...*/
    private static MyResourceType MyResource = new MyResourceType();

    public void DoSomething()
    {
         lock(_gate)
         {
            /* do something with MyResource, just make sure you
               DO NOT call another method that locks the gate
               i.e. this.DoSomethingElse(), in those situations,
               you can take the logic from DoSomethingElse() and
               toss it in a private method i.e. _DoSomethingElse().
             */
         }
    }

    private void _DoSomethingElse()
    {
        /* do something else */
    }

    public void DoSomethingElse()
    {
         lock(_gate)
         {
             _DoSomethingElse();
         }
     }
}

その日遅く...

var myClass = new MyClass();
myClass.DoSomething();

ロックを使用して複数のメソッドを呼び出せるようにする場合は、ラムダを使用して呼び出すことができます。本当に安全な方法として、ヘルパークラスでラップします。

public class MyClass
{
    public MyResourceType MyResource { get; set; }
    public void DoFirstThing() { ... }
    public void DoSecondThing(){ ... }
}

public class MyClassHelper
{
    private static readonly object _gate = new Object();
    private static MyResourceType MyResource = new MyResourceType();

    private MyClass _myClass = new MyClass();        

    public void DoWork(Action<MyClass> action)
    {
         lock(_gate)
         {
             _myClass.MyResource = MyResource;
             action(_myClass);
             _myClass.MyResource = null;
         }
    }
}

...

var myClassHelper = new MyClassHelper();
myClassHelper.DoWork(x => 
    {
        x.DoFirstThing();
        x.DoSecondThing();
    });
于 2012-08-02T15:22:50.713 に答える
1

あなたの例から、あなたがやろうとしていることは明確ではありません。優れたプログラミング手法として、個々のメソッドでロックを取得し、クリティカル セクションの処理が完了したら解放することをお勧めします。あなたの場合は次のようになります。

void doStuff()
{
    if(isDisposed) throw new ObjectDisposedException();
    // do stuff here, etc.
    lock(_locker) { 
       // enter critical section here 
    }
    // continue to do other stuff
}

void doStuff2()
{
    if(isDisposed) throw new ObjectDisposedException();
    // do stuff here, etc.
    lock(_locker) { 
       // enter critical section here 
    }
    // continue to do other stuff
}

現在、lock は Monitor クラスを使用するためのショートカット バージョンです。そして、実際には次のように翻訳されます。

bool getLock = false;
try {
  Monitor.Enter(locker, ref getLock);
  // do stuff here
}
finally {
  if(getLock) {
    Monitor.Exit(locker);
  }
}

これにより、オブジェクトの状態と内部表現をより詳細に制御できます。何か問題が発生した場合、オブジェクトの以前の状態に戻すことができます。

于 2012-08-02T15:07:37.703 に答える
1

lock は、Monitor.Enter および Exit を直接使用するよりも簡単で、エラーが発生しにくくなっています。

あなたの例からは、何を同期しようとしているのか明確ではありません。

コンストラクターで Monitor.Enter を実行し、Dispose で終了することはお勧めできません。 クラスを適切に構築できない場合は、c'tor 内ですべての例外を処理し、Exit を呼び出す必要があります。インスタンスがロックされることは意味がありません。これは本質的に、c'tor をロックすることを意味します。Synchronized 属性を確認することをお勧めします。しかし、それは本当にお勧めではないと思います。

于 2012-08-02T14:21:13.120 に答える
1

静的オブジェクトに対する他のオブジェクトからのリクエストをすぐに実行することは重要ですか? 静的オブジェクトがそれ自体が処理するキューを保持している場合は、スレッド分離によって相互排除を実現できます。別のオブジェクトからの呼び出しでは、要求された作業がキューに置かれますが、別のスレッドでは、静的オブジェクトがキューを介して処理されます (ただし、キューへの相互排他的アクセスが必要であることに注意してください!) 要求を実行します。

静的オブジェクトから通知されるまで、作業をキューに追加したメソッドで呼び出し元オブジェクトをブロックするか、コールバック インターフェイスを提供して、静的オブジェクトが呼び出し元オブジェクトに作業が完了したことを通知できるようにすることができます。

于 2012-08-02T14:29:06.060 に答える
1

呼び出しの組み合わせがあまりない場合は、dostuff と doStuff2 を非公開にして、クラスにロック付きのラッパー関数を配置できます。

static public void doStuffs()
{
    lock (lockObject)
    {
        doStuff();
        doStuff2();
    }
}
于 2012-08-02T15:31:16.530 に答える