2011-01-26 13 views
4

私は同期(ロック)のために使用するプライベート静的フィールドを持っています。今私は同時に実行したくない2つの機能を持っています。だから私はこのでした:私はオブジェクトにロックすると、単一の関数の並列実行を防ぐことができますことを知っているC#スレッドと同期

public class Synchronization 
{ 
    private static object _lock = new object(); 

    public void MethodA() 
    { 
     lock (_lock) 
     { 
      Console.WriteLine("I shouldn't execute with MethodB"); 
     } 
    } 

    public void MethodB() 
    { 
     lock (_lock) 
     { 
      Console.WriteLine("I shouldn't execute with MethodA"); 
     } 
    } 
} 

を、同じ仕事、私は同時に実行するさまざまな方法で同じロックオブジェクトを使用する場合は?単純に、他のスレッドが既にロックされているオブジェクトを別の関数でロックすることはできますか?

+2

? –

+3

また、この方法では、クラスのすべてのインスタンスを同期していることに注意してください。必要がない場合は、ロックオブジェクトの「静的な」部分を削除します。 –

+1

@Andreas MTの理論と実践は難しい - それは有益な提案ではない。 –

答えて

5

一度に1つのスレッドのみがロックを取得できるため、この状態は1つのロックインスタンス上のすべてのスレッドに対して排他的です。したがって、あなたの例では、あなたのロックが静的であるので、クラスSynchronizationのすべてのインスタンスに対して、指定された時間に1つのメソッド本体しか実行されないかもしれません。クラスのインスタンスごとにロックを設定する場合は、ロックオブジェクトを静的としてマークしないでください。

同期に関するあなたの仮定は正しいです。

ロックオブジェクトreadonlyを完全に水密なソリューションにする必要があることに注意してください。コードは、現状ではロックオブジェクトを再割り当てられ、そのロックのセマンティクスを破るされるために、それは例えば、可能でしょう:

public class Synchronization 
{ 
    private static object _lock = new object(); 

    public void MethodA() 
    { 
     lock (_lock) 
     { 
      Console.WriteLine("I shouldn't execute with MethodB"); 
     } 
    } 

    public void MethodB() 
    { 
     //This shouldn't be allowed! 
     _lock = new object(); 

     lock (_lock) 
     { 
      Console.WriteLine("I shouldn't execute with MethodA"); 
     } 
    } 
} 

ロックオブジェクトはreadonly、すなわちとしてマークする必要があります。

private static readonly object _lock = new object(); 
+0

あなたは静的ロックを扱っているので ''特定のインスタンスの ''で' 'メソッドボディ'のみを強化するべきです:) –

+0

'readonly' ... !!!神様! –

+0

@Andreas私は静的ロックの意味を詳述しました。 –

1

正しく実行しています。同じ時間に入力されない2つの重要なセクションを作成しました。

したがって、MethodAとMethodBは同時にアクティブではありません。また、MethodA(とMethodB)が同時にアクティブになるのは1つだけです。

これは、作成するすべてのオブジェクトにわたって有効です。私が意味すること:任意のオブジェクトから任意のMethodAまたはMethodBに1つのスレッドだけがあります。ロックを1つのオブジェクト内でのみ実行する場合は、_lockオブジェクトを静的にすることはできません。

1

ロックは、lockステートメントが発生するメソッドではなく、ロックの対象となるオブジェクトに基づいて付与されます。あなたの場合、複数のスレッドがさまざまなメソッドを入力するかもしれませんが、一度に1つのスレッドだけがlockステートメント内のコードを実行することができます。

1

まず、_lockは静的であってはなりません。または、オブジェクトの複数のインスタンスを互いにロックする必要がありますか?次に、クラス内に同期メソッドを1つだけ持つ必要があります。さらに、クラス内の同期メソッド間の依存関係を避ける必要があります。そうしないと、メソッドの呼び出し元が間違って予期しない動作をするリスクがあります。

考えてみては、例えば、次のコード:

class Synchronized 
{ 
    object lockObj = new object(); 
    int counter = 100; 

    public void Decrement() 
    { 
     lock (this.lockObj) 
     { 
      this.counter--; 
     } 
    } 

    public int IsZero() 
    { 
     lock (this.lockObj) 
     { 
      return this.counter == 0; 
     } 
    } 
} 

は今1は、共有同期インスタンスに何をしますか?この

while (!synchronized.IsZero()) 
{ 
    synchronized.Decrement(); 
} 

今すぐ1つの通話デクリメントを通すよう

は、それを使用して、カウンタが0になると、それはデクリメント方法、ないIsZero方法でロックを待機していたので、すぐに、2つの通話デクリメントを通します。カウンタは-1になり、ループは無限になります。

ロック機構が誤ってプログラムされているのではなく、呼び出し元がそれをうまく使用していないとは限りません。 Synchronizedクラスで1つの同期メソッドしか公開していない場合は、プログラマが安全だと盲目的に信じることを欺くことはありません。

それはこのようなものでなければなりません:なぜ、単純に自分でそれを試してみていない

class Synchronized 
{ 
    object lockObj = new object(); 
    int counter = 100; 

    public bool IfNotZeroDecrement() 
    { 
     lock (this.lockObj) 
     { 
      if (this.counter > 0) 
       this.counter--; 

      return this.counter > 0; 
     } 
    }  
} 

/// Usage: 
while (synchronized.IfZeroDecrement()) 
{ 
} 
+0

私はクラス内の同期化されたメソッドに同意しません。あなたの例は貧弱な実装です。 0より小さい値を減らすことができないことが重要な場合は、それに対処する必要があります。 – btlog

+1

@btlogこの簡単な例では明らかですが、より複雑なシナリオではそれは明らかではありません。コードのユーザにすべての責任を負わせるべきではありません。さもなければ、後でデバッグするのが困難な深刻な問題に陥る可能性があります。それを安全かつ清潔にして、誰もが将来それから恩恵を受けるでしょう。 –

+0

これについての推奨読者(および他の人):http://blog.objectmentor.com/articles/2008/04/08/clean-code-whew "メソッド間の依存関係が同時コードを破る" –