マルチスレッドに深く入り込む:ReaderとWrite Locks(読み書きロック)の使用詳細


スレッドセキュリティのための通常の要求は、併読は許可されていますが、併記は許可されていません。例えばファイルについては、このようなものです。
ReaderWriter LockSlimは.net frame ework 3.5の時に提供しました。以前の「fat」バージョンに代わる「ReaderWriterLock」です。
この二つの種類には、基本的な鍵が二つあります。
書き込みロックは完全に彼のロックです。
リードロックは他のリードロックと互換性があります。
したがって、スレッドが書き込みロックを持っている場合は、すべてのスレッドが読み取りロックと書き込みロックのスレッドをブロックしますが、書き込みロックを持っていない場合は、一連のスレッドが同時に読み取りロックを取得することができます。
Reader Writer LockSlimは、次のいくつかの方法を定義し、読み書きロックを取得し、解放する。
    Public void EnterReadLock();    Public void Exit ReadLock()    Public void EnterWriteLock()    Public void Exit WriteLock()
Monitor.TryEnterと同様に、ReaderWriter LockSlimに対応した「EnterXXX」仕様にも対応した「Try」バージョンが提供されています。ReaderWriterLockはAcquire XXXとRelease XXXの方法を提供しました。タイムアウトが発生した時、ReaderWriterLockはApple Exceptionを投げました。falseに戻るのではありません。

static readonly ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();
        static List<int> _items = new List<int>();
        static Random _rand = new Random();

        public 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(100);
                _rw.ExitReadLock();//
            }
        }

        static void Write(object threadID)
        {
            while (true)
            {
                Console.WriteLine(_rw.CurrentReadCount + " concurrent readers");

                int newNumber = GetRandomNum(100);

                _rw.EnterWriteLock(); //
                _items.Add(newNumber); //
                _rw.ExitWriteLock();  //
                Console.WriteLine("Thread " + threadID + " added " + newNumber);

                Thread.Sleep(100);
            }
        }

        //
        static int GetRandomNum(int max) { lock (_rand) return _rand.Next(max); }

実際のリリースバージョンでは、try/finallyを使用して、異常が出てもロックが正しく解除されることを確認したほうがいいです。

CurrenntReadCount属性のように、ReaderWriterLockSlimは以下の属性を提供して、ロックを監視します。

更新可能な鍵:
もう一つの原子操作では、読み書きロックにアップグレードすることが有用です。例えば、もう一つのlistに存在しない項目を書いてほしいなら、次のステップを実行します。
リードロックを取得します。
テストは、書くものがリストにあるとロックを解除して戻ってきます。
リードロックを解除します。
書き込みロックを取得します
項目を追加して、ものを書きます。
書き込みロックを解除する。
問題は、第3ステップと第4ステップの間に、他のスレッドがリストを修正した可能性があることである。
ReaderWriterLockSlimは、アップデートロックというもので、この問題を解決します。
更新可能な鍵は原子操作に加えて、書き込みロックになります。読みロックのようなものです。このように使えばいいです。
EntterUpgradeable ReadLockを呼び出して更新可能なロックを取得します。例えば、書くものがListにないと判断するために、いくつかの読み操作を実行します。EnterWriteLockを呼び出します。この方法は更新可能ロックを書き込みロックにアップグレードします。書き込み操作を実行して、ExitWriteLock方法を呼び出します。この方法は書き込みロックを更新可能ロックに変換します。読み作業を続けます。何もしません。
ExitUpgradeable ReadLockを呼び出してロックを更新できます。
調合者の観点から見れば、ネスト/再帰ロックのように見えます。機能的には第三段階で、
ReaderWriter LockSlimは原子操作において読み書きロックを解除し、書き込みロックを取得する。
更新可能ロックと読み出しロックの重要な違いは、更新可能ロックと読み出しロックが共存していますが、一度だけ更新可能ロックが取得されます。このような主な目的はロック防止です。
このようにWrite方法を修正して、リストにないItemを追加できます。

static void Write(object threadID)
        {
            while (true)
            {
                Console.WriteLine(_rw.CurrentReadCount + " concurrent readers");

                int newNumber = GetRandomNum(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);
            }
        }

は上の例からC〓の提供する読み書きロックの機能が強くて、使いやすいです。
自分で読み書きロックを作るときは、更新可能ロックをサポートする必要があるかどうかを考え、自分で読み書きロックを書く必要がありますか?