2013-06-11 8 views
11

私のプロパティへのアクセスを同期させるために、ReaderWriterLockSlimクラスを使用します。スレッドセーフな方法でプロパティにアクセスするには、次のコードを使用します。ReaderWriterLockSlim.IsReadLockHeld/IsWriteLockHeldに対するマイクロソフトの発言とその結果

public class SomeClass 
{ 
    public readonly ReaderWriterLockSlim SyncObj = new ReaderWriterLockSlim(); 
    public string AProperty 
    { 
     get 
     { 
      if (SyncObj.IsReadLockHeld) 
       return ComplexGetterMethod(); 
      SyncObj.EnterReadLock(); 
      try 
      { 
       return ComplexGetterMethod(); 
      } 
      finally 
      { 
       SyncObj.ExitReadLock(); 
      } 
     } 
     set 
     { 
      if (SyncObj.IsWriteLockHeld) 
       ComplexSetterMethod(value); 
      else 
      { 
       SyncObj.EnterWriteLock(); 
       ComplexSetterMethod(value); 
       SyncObj.ExitWriteLock(); 
      } 
     } 
    } 

    // more properties here ... 

    private string ComplexGetterMethod() 
    { 
     // This method is not thread-safe and reads 
     // multiple values, calculates stuff, ect. 
    } 

    private void ComplexSetterMethod(string newValue)  
    { 
     // This method is not thread-safe and reads 
     // and writes multiple values. 
    } 
} 

// ===================================== 

public static SomeClass AClass = new SomeClass(); 
public void SomeMultiThreadFunction() 
{ 
    ... 
    // access with locking from within the setter 
    AClass.AProperty = "new value"; 
    ... 
    // locking from outside of the class to increase performance 
    AClass.SyncObj.EnterWriteLock(); 
    AClass.AProperty = "new value 2"; 
    AClass.AnotherProperty = "..."; 
    ... 
    AClass.SyncObj.ExitWriteLock(); 
    ... 
} 

私が手やかつて私はReaderWriterLockSlim -objectを発表し、クラスの外から、私はプロパティの束を取得または設定しようと思いたびにそれをロックする複数のプロパティを設定するたびに、不要なロックを回避するために。これを実現するには、getterメソッドとsetterメソッドを使用して、IsReadLockHeldプロパティとIsWriteLockHeldプロパティを使用してロックが取得されているかどうかを確認してください(ReaderWriterLockSlim)。これは正常に動作し、私のコードのパフォーマンスが向上しました。

これまでのところは良いが、私は時期についてIsReadLockHeldIsWriteLockHeldドキュメントを再読み込み、私は発言フォームマイクロソフトに気づい:

このプロパティは、アサートまたはその他のデバッグ の目的のために使用するためのものです。プログラムの実行フローを制御するために使用しないでください。

私の質問は:なぜ、この目的のためにIsReadLockHeld/IsWriteLockHeldを使用してはならない理由はありますか?私のコードに何か問題はありますか?再帰的ロック(LockRecursionPolicy.SupportsRecursion)を使用するよりも、すべてが期待どおりに動作します。

これを明確にする:これは最小の例です。私は、ロック自体が必要かどうか、あるいは別の方法で取り除くことができるかどうかを知りたくありません。私はちょうどなぜIsReadLockHeld/IsWriteLockHeldを使用して、ドキュメンテーションの記述通りにプログラムの流れを制御すべきではないかを知りたいと思っています。

+0

ReaderWriteLockSlimは何も保護しません。.NETは既に、オブジェクト参照の更新がアトミックであることを約束しています。それが持つ唯一のマイルドな副作用は、セッターの更新が他のスレッドで見えるということです。 MemoryBarrier()を使用するほうがはるかに安いです。スレッドセーフの保証はありません.SyncObjは誤った安全感しか与えないので、SyncObjを完全に削除する方が良いでしょう。 –

+0

@EricLippert:IsRead/WriteLockHeldのドキュメントは、現在のスレッド*がロックを保持しているときに 'true'を返すことを示唆しています。 'IsRead/WriteLockHeld'がtrueを返す場合、' ExitRead/WriteLock'への明示的な呼び出しなしに、そのスレッドでロック状態がどのように変更されますか? – Iridium

+0

@イリジウム:私のコメントを撤回します。私は今、文書がその警告をなぜ出さないのか、実際は分かりません。つまり、ベストプラクティスはドキュメンテーションが言っていることをすることです。 –

答えて

12

さらなる調査の後、同じ質問をGerman Support Forum of the Microsoft Developer Networkに掲載し、非常に役立つモデレータと相談しました。Marcel Roma。私は私の答えが望まれるために何かを残すことが怖い

:彼はこの答えを書いたReaderWriterLockSlimジョーダフィーのプログラマーに連絡することができました。

プロパティは正常に動作し、文書化されています。条件付きのロックの取得と解放は、 であり、実際にはエラーが発生しやすい傾向があります。特に、 に投げ込まれた例外を使用すると、ガイダンスは実際には になります。

再帰的な取得を使用するようにコードを構造化することをお勧めします。そうでない場合は、後で の方が常に簡単です。 IsReadLockHeldのようなプロパティを使用してください。 あなたはどこかの中間にいます。

私はRWLSの主なデザイナーの1人でしたが、私はそれが あまりにも多くの鐘や笛を持っていることを認めなければなりません。私は必ずしも IsReadLockHeldを追加することを後悔していません - デバッグとアサーションのために便利なので、 - それを追加すると、Pandoraのボックスが開き、RWLSがすぐにこの種の使い方まで開かれました。

私は人々が StackOverflowの糸のようにそれを使用することを驚いていないよ、と私はそれが代替案よりも良い作品いくつかの合法的なシナリオ があると確信しています。私は単に を誤って使用していないことをアドバイスします。

物事をまとめると:あなたは条件付きでロックを獲得するために、すべてが正常に動作しますIsReadLockHeldIsWriteLockHeldプロパティを使用することができますが、それは悪いプログラミングスタイルで、もう1つは、それを避ける必要があります。再帰的または非再帰的なロックに固執する方がよい。 IsReadLockHeldIsWriteLockHeldは、デバッグの目的でのみ使用してください。

Marcel RomaとJoe Duffyに感謝したいと思います。

-2

文書で正しいことを伝えています。

次のインターリーブされた実行を考慮してください。

Thread1.AcqrireReadLock(); 
Thread1.ComplexGetterMethod(); 
Thread2.ReadIsReaderLockHeldProperty(); 
Thread1.ReleaseReadLock(); 
Thread2.ComplexGetterMethod(); // performing read without lock. 

私が見るあなたのコードと他の間違ったことは

SyncObj.EnterReadLock(); 
try 
{ 
    return ComplexGetterMethod(); 
} 
finally 
{ 
    SyncObj.ExitReadLock(); 
} 

物事を行うには正しい方法ではありませんです。これは正しいものです:

try 
{ 
    SyncObj.EnterReadLock(); 

    return ComplexGetterMethod(); 
} 
finally 
{ 
    if (SyncObj.IsReadLockHeld) 
     SyncObj.ExitReadLock(); 
} 

これはゲッターメソッドの正確な定義になります。

+0

'IsReadLockHeld'は**現在のスレッド**がロックを保持しているときだけ' true'を返すので、そのような実行は起こりませんでした。 – svick

+0

私の間違い。あなたが正しいです。 – Egor

関連する問題