同時データ(ロック)Reader WriterLockSlim

8711 ワード

通常、1つのタイプのインスタンスは、パラレルリード操作に対してスレッドが安全であるが、パラレル更新操作はそうではない(パラレルリードと更新もそうではない).これは、リソース(ファイルなど)にも同様です.単純な独占ロックを使用して、可能なすべてのアクセスをロックすると、インスタンスのスレッドセキュリティが問題になりますが、偶然の更新操作で多くの読み取り操作がある場合、これは同時性を制限するのに合理的ではありません.1つの例は、ビジネス・プログラム・サーバで、データをすばやく検索するために静的フィールドにキャッシュすることです.このような場合、ReaderWriterLockSlimクラスは、最大可能なロックを提供するように設計される.
Reader WriterLockSlimには、2つの基本的なロック方法があります.1つの独占的なWirteロックと、他のRead lockに適合するリードロックです.
したがって、1つのスレッドがWrite Lockを持つと、他のすべてのスレッドが読み書きロックを取得するのをブロックします.ただし,WriteLockを取得するスレッドがない場合には,ReadLockを同時に取得して読み出しを行う複数のスレッドがある.
Reader WriterLockSlimは、次の4つの方法で読み書きロックを取得および解放します.
public void EnterReadLock(); 
public void ExitReadLock();
public void EnterWriteLock();
public void ExitWriteLock();

 
また、すべてのEnterXXXメソッドについては、MonitorのようにtimeOutパラメータを受信する「Try」バージョンのメソッドもあります.TryEnter同様(リソース競合が深刻な場合はタイムアウトが発生しやすい).さらにReader WriterLockは、falseを返すのではなく、タイムアウトしたときに異常を投げ出す他の類似のAcquireXXXおよびReleaseXXXメソッドを提供しています.
次のプログラムでは、Reader WriterLockSlimを示します.3つのスレッドが1つのリストをループして列挙し、他の2つのスレッドが1秒ごとにリストにランダム数を追加します.read lockはListのリードスレッドを保護し、write lockはライトスレッドを保護します.
class SlimDemo 
{
static ReaderWriterLockSlim rw = new ReaderWriterLockSlim();
static List<int> items = new List<int>();
static Random rand = new Random();

static void Main()
{
new Thread (Read).Start();
new Thread (Read).Start();
new Thread (Read).Start();

new Thread (Write).Start ("A");
new Thread (Write).Start ("B");
}

static void Read()
{
while (true)
{
rw.EnterReadLock();
foreach (int i in items) Thread.Sleep (10);
rw.ExitReadLock();
}
}

static void Write (object threadID)
{
while (true)
{
int newNumber = GetRandNum (100);
rw.EnterWriteLock();
items.Add (newNumber);
rw.ExitWriteLock();
Console.WriteLine ("Thread " + threadID + " added " + newNumber);
Thread.Sleep (100);
}
}

static int GetRandNum (int max) { lock (rand) return rand.Next (max); }
}
// try/finally, lock 。

結果:
Thread B added 61 
Thread A added 83
Thread B added 55
Thread A added 33
...

Reader WriterLockSlimは、単純なLockよりも大きな同時読解能力を可能にします.Whileループの開始時にWriteメソッドにコードを追加できます.
Console.WriteLine (rw.CurrentReadCount + " concurrent readers");

基本的には常に「3 concurrent readers」が返されます(読み方はForeachサイクルでより多くの時間を費やします).Reader WriterLockSlimは、CurrentReadCountプロパティと同様のプロパティを多く提供し、lockの状況を監視します.
public bool IsReadLockHeld            { get; } 
public bool IsUpgradeableReadLockHeld { get; }
public bool IsWriteLockHeld { get; }

public int WaitingReadCount { get; }
public int WaitingUpgradeCount { get; }
public int WaitingWriteCount { get; }

public int RecursiveReadCount { get; }
public int RecursiveUpgradeCount { get; }
public int RecursiveWriteCount { get; }

あるitemがlistに存在しない場合、このitemを追加するなど、1つの原子操作で読み書きロックを交換することは非常に有用である場合があります.最良の場合、ロックのように書く時間を最小化し、例えば以下のように処理する.
1リードロックを取得
2リストにitemが含まれているかどうかをテストし、そうであれば返します.
3リードロックを解除する
4書き込みロックを取得
5 itemをlistに書き込み、書き込みロックを解除します.
しかし、ステップ3、4の間で、別のスレッドがリストをこっそり修正する可能性がある場合(例えば、同じItemを追加する)、Reader WriterLockSlimは、第3のロックを提供することによってこの問題を解決する.これがupgradeable lockである.アップグレード可能なロックはread lockと似ていますが、原子操作でwrite lockにアップグレードできます.使用方法は次のとおりです.
  •  
  • EnterUpgradeableReadLock
  • を呼び出す
  • リードオペレーション(e.g.test if item already present in list)
  • EnterWriteLock(this converts the upgradeable lock to a write lock)
  • を呼び出す
  • 書き込み操作(e.g.add item to list)
  • ExitWriteLock(this converts the write lock back to an upgradeable lock)
  • を呼び出す
  • その他の読み出しプロセス
  • ExitUpgradeableReadLock
  • を呼び出す

    呼び出し者の立場から、これは非常に再帰的(ネストされた)ロックになりたいと思っています.実際に3ステップ目の時、1つの原子操作によってread lockを放出し、新しいwrite lockを得た.
    upgradeable locksとread locksの間にはもう一つの重要な違いがあります.1つのupgradeable locksは任意の複数のread locksと共存することができますが、1つの時点では、1つのupgradeable lockだけが自分で使用されます.これはデッドロックを防止した.SQL ServerのUpdate lockと似ています
    前の例のWriteメソッドを変更してupgradeable lockを示すことができます.
    while (true) 
    {
    int newNumber = GetRandNum (100);
    rw.EnterUpgradeableReadLock();
    if (!items.Contains (newNumber))
    {
    rw.EnterWriteLock();
    items.Add (newNumber);
    rw.ExitWriteLock();
    Console.WriteLine ("Thread " + threadID + " added " + newNumber);
    }
    rw.ExitUpgradeableReadLock();
    Thread.Sleep (100);
    }

    Reader WriterLockはupgradeable locksの機能を提供していません.
     
     
     
    Reader WriterLockSlimクラスでは、Read、Write、UpgradeableReadの3つのロックモードがサポートされています.この3つのパターンに対応する方法は,それぞれEnterReadLock,EnterWriteLock,EnterUpgradeableReadLockである.さらにこれに対応するTryEnterReadLock,TryEnterWriteLock,TryEnterUpgradeableReadLock,ExitReadLock,ExitWriteLock,ExitUpgradeableReadLockである.ReadとWriterロックモードは比較的簡単で分かりやすい:Readモードは典型的な共有ロックモードであり、任意の数のスレッドがこのモードで同時にロックを得ることができる.Writerモードは反発モードであり,このモードでは1つのスレッドのみがロックに入ることを許可する.UpgradeableReadロックモードは多くの人にとって新鮮かもしれませんが、データベースの分野ではよく知られています.
    この新しい読み書きロッククラスの性能はMonitorクラスとほぼ同等で,Monitorクラスの約2倍以内である.また、新しいロックは、書き込み動作の周波数が読み取り動作よりはるかに小さいため、書き込みスレッドにロックを優先させる.通常、これはより良い伸縮性をもたらします.当初,ReaderWriterLockSlimクラスは設計時にかなりの状況を考慮していた.例えば、初期CTPのコードには、PrefersReaders、PrefersWritersAndUpgrades、Fifoなどの競合戦略も提供されています.しかし、これらのポリシーは追加するのは簡単ですが、状況が非常に複雑になります.したがって、Microsoftは最後に、多くの場合、良好に動作する簡単なモデルを提供することを決定しました.
     
    #スレッドは、読み取りモード、書き込みモード、およびアップグレード可能な読み取りモードの3つのロックモードに入ることができます.
    #アップグレード可能モードは、スレッドが保護されたリソースの内容を通常読み出す場合に適用されますが、条件が満たされた場合に書き込みが必要になる場合があります.アップグレード可能なロックを使用すると、読み取りロックから書き込みロックに簡単にアップグレードでき、切り替えを必要とせず、損失を増加させることができます.
    #通常、Reader WriterLockSlimのインスタンスをtry{}ブロックに配置しないで、パフォーマンス損失を低減します.
    例:
    public 
    class
     ReaderWriterLockSlimSample{    
    private 
    static
     ReaderWriterLockSlim rwLock 

    new
     ReaderWriterLockSlim();    
    private 
    object
     Get()    {        
    object
     obj 

    null
    ;        
    if
     (rwLock.TryEnterReadLock(
    100
    ))        {            
    try
                {                
    //
    書き込み操作
                    obj 

    new 
    object
    ();                
    return
     obj;            }            
    finally
                {                rwLock.ExitReadLock();            }        }        
    return 
    null
    ;    }    
    private 
    void
     Add()    {        
    if
     (rwLock.TryEnterWriteLock(
    100
    ))        {            
    try
                {                
    //
    書き込み操作
                }            
    finally
                {                rwLock.ExitWriteLock();            }        }    }    
    public 
    void
     Update()    {        
    if
     (rwLock.TryEnterUpgradeableReadLock(
    100
    ))        {            
    try
                {                
    //
    リードオペレーション
                    rwLock.EnterWriteLock();                
    try
                    {                    
    //
    書き込み操作
                    }                
    finally
                    {                    rwLock.ExitWriteLock();                }            }            
    finally
                {                rwLock.ExitUpgradeableReadLock();            }        }    }}