2011-01-16 5 views
1

私はサーバーに送信したいオブジェクトがいくつかありますが、これがStageからUploadにデータを移動する唯一のスレッドであることを確認したいと思います。次のコードはマルチスレッド環境で有効ですか?C#でLocked()されたオブジェクトを上書きすることはできますか?

 List<CounterInternal> UploadToServer = new List<CounterInternal>(); 
     List<CounterInternal> StagingQueue = new List<CounterInternal>(); 
     lock (this.UploadToServer) 
     lock (this.StagingQueue) 
     { 
      if (UploadToServer.Count == 0) 
      { 
       UploadToServer = StagingQueue.DoDeepCopyExtensionMethod(); 
       // is the following line valid given that I have a Lock() on it? 
       StagingQueue = new List<CounterInternal>(); 
      } 
     } 
     } 
+0

コードは書かれていますが、F5ボタンが見つからないため質問を投稿する時間がかかりましたか? –

+1

私はそれをテストしていますが、そこに他の隠れたものがあるかもしれないと思っています。たとえば、ロックが取られないか、上書きされた後に消えるか、データが失われる可能性があります。私はエッジのケースを考えているが、私は新しいので、私は何を尋ねるのか分からない可能性があります。質問を文字通り取って、私の意図を見てはいけません。 – LamonteCristo

+0

私はあまりにも皮肉なものとして出くわしていませんでした。私が謝罪した場合、私はちょうど(そして多分それはあなたがそこでの方法の99%であることを知っています)、ちょうどそれを試して見てみませんか?そして@Andyが言及したように、[lock](http://msdn.microsoft.com/en-us/library/c5kehkcz%28v=vs.71%29.aspx)は参照を保持しているので、良い考えではありませんそれがロックされている間にそれを変更する。 –

答えて

4

技術的には、これは悪い考えです。

using System; 

class Foo { 
    static object foo = new object(); 
    static void Main() { 
     lock (foo) { 
      foo = new object(); 
     } 

    } 
} 

Main()方法がにコンパイルされます:

.method private static hidebysig 
     default void Main() cil managed 
{ 
    // Method begins at RVA 0x2100 
    .entrypoint 
    // Code size 35 (0x23) 
    .maxstack 3 
    .locals init (
      object V_0) 
    IL_0000: ldsfld object Foo::foo 
    IL_0005: stloc.0 
    IL_0006: ldloc.0 
    IL_0007: call void class [mscorlib]System.Threading.Monitor::Enter(object) 
    .try { // 0 
     IL_000c: newobj instance void object::'.ctor'() 
     IL_0011: stsfld object Foo::foo 
     IL_0016: leave IL_0022 

    } // end .try 0 
    finally { // 0 
     IL_001b: ldloc.0 
     IL_001c: call void class [mscorlib]System.Threading.Monitor::Exit(object) 
     IL_0021: endfinally 
    } // end handler 0 
    IL_0022: ret 
} // end of method Foo::Main 

これは(手で逆コンパイル)以下のソースに対応する:

static void Main() { 
    object V_0 = foo; 
    Monitor.Enter(V_0); 
    try { 
     foo = new object(); 
    } finally { 
     Monitor.Exit(V_0); 
    } 
} 

だからオブジェクトこれC#ソースファイルを検討がロックされている場合は、ローカルに格納されます。これにより、フィールドに格納されているオブジェクト参照が置き換えられてもオブジェクトのモニタが解放されます。この方法だけではデッドロックは発生せず、すでにMonitor.Enter()にブロックされている他のスレッドは、このスレッドがロックを解除するまで通常どおりブロックし続けます。しかし

、あなたはアクティブなスレッドのリリースはロックが新しいオブジェクトのロックを取得することになるので、に2つのスレッドがあることができ前にオブジェクトが、を再割り当てしている後、この方法に入り、任意のスレッド同時にロックブロック。

もっと良い解決策は、別のオブジェクトを使用して代わりにロックすることです。私は通常、クラスSystem.Object(またはちょうどobject)の何かを使用しているのは、それが行うことがmutexとして動作しているからです。これにより、すべてのスレッドが同じオブジェクトにロックされ、他のオブジェクト参照が変更されるようになります。ロックされていない値の型を変更するためにロックする必要がある場合にも、これは便利な方法です。

+0

ある日私はあなたが上でやったことを読んで手動でやることを学ぶ必要があります!ありがとう!それをどうやって学んだのですか? – LamonteCristo

+0

何をする? ILを手作業で逆コンパイルするか、最初にILを入手するだけですか? – cdhowie

+0

手で逆コンパイル... – LamonteCristo

0

これはお勧めできません。 lockは特定のインスタンスで動作し、new List<>を実行すると新しいインスタンスを作成します。別のスレッドがロックされた領域であると考えられている間に、別のスレッドが新しいStagingQueueをロックしてロックする可能性があります。

あなたがすべきいずれかの別のインスタンス変数(好ましくreadonlyもの)、ロックされたオブジェクトであることが純粋に存在する多分Objectインスタンスに

  1. lock
  2. List<>の新しいインスタンスを作成しないようにアルゴリズムを変更します。たとえば、リストを空にするだけの場合は、StagingQueue.Clear()を呼び出します。
関連する問題