官术网_书友最值得收藏!

Using the ReaderWriterLockSlim construct

This recipe will describe how to create a thread-safe mechanism to read and write to a collection from multiple threads using a ReaderWriterLockSlim construct. ReaderWriterLockSlim represents a lock that is used to manage access to a resource, allowing multiple threads for reading or exclusive access for writing.

Getting ready

To step through this recipe, you will need Visual Studio 2015. There are no other prerequisites. The source code for this recipe can be found at BookSamples\Chapter2\Recipe8.

How to do it...

To understand how to create a thread-safe mechanism to read and write to a collection from multiple threads using the ReaderWriterLockSlim construct, perform the following steps:

  1. Start Visual Studio 2015. Create a new C# console application project.
  2. In the Program.cs file, add the following using directives:
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using static System.Console;
    using static System.Threading.Thread;
  3. Below the Main method, add the following code:
    static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();
    static Dictionary<int, int> _items = new Dictionary<int, int>();
    
    static void Read()
    {
      WriteLine("Reading contents of a dictionary");
      while (true)
      {
        try
        {
          _rw.EnterReadLock();
          foreach (var key in _items.Keys)
          {
            Sleep(TimeSpan.FromSeconds(0.1));
          }
        }
        finally
        {
          _rw.ExitReadLock();
        }
      }
    }
    
    static void Write(string threadName)
    {
      while (true)
      {
        try
        {
          int newKey = new Random().Next(250);
          _rw.EnterUpgradeableReadLock();
          if (!_items.ContainsKey(newKey))
          {
            try
            {
              _rw.EnterWriteLock();
              _items[newKey] = 1;
              WriteLine($"New key {newKey} is added to a dictionary by a {threadName}");
            }
            finally
            {
              _rw.ExitWriteLock();
            }
          }
          Sleep(TimeSpan.FromSeconds(0.1));
        }
        finally
        {
          _rw.ExitUpgradeableReadLock();
        }
      }
    }
  4. Inside the Main method, add the following code:
    new Thread(Read){ IsBackground = true }.Start();
    new Thread(Read){ IsBackground = true }.Start();
    new Thread(Read){ IsBackground = true }.Start();
    
    new Thread(() => Write("Thread 1")){ IsBackground = true }.Start();
    new Thread(() => Write("Thread 2")){ IsBackground = true }.Start();
    
    Sleep(TimeSpan.FromSeconds(30)); 
  5. Run the program.

How it works...

When the main program starts, it simultaneously runs three threads that read data from a dictionary and two threads that write some data into this dictionary. To achieve thread safety, we use the ReaderWriterLockSlim construct, which was designed especially for such scenarios.

It has two kinds of locks: a read lock that allows multiple threads to read and a write lock that blocks every operation from other threads until this write lock is released. There is also an interesting scenario when we obtain a read lock, read some data from the collection, and, depending on that data, decide to obtain a write lock and change the collection. If we get the write locks at once, too much time is spent, not allowing our readers to read the data because the collection is blocked when we get a write lock. To minimize this time, there are EnterUpgradeableReadLock/ExitUpgradeableReadLock methods. We get a read lock and read the data; if we find that we have to change the underlying collection, we just upgrade our lock using the EnterWriteLock method, then perform a write operation quickly and release a write lock using ExitWriteLock.

In our case, we get a random number; we then get a read lock and check whether this number exists in the dictionary key collection. If not, we upgrade our lock to a write lock and then add this new key to a dictionary. It is a good practice to use try/finally blocks to make sure that we always release locks after acquiring them.

All our threads have been created as background threads, and after waiting for 30 seconds, the main thread as well as all the background threads get completed.

主站蜘蛛池模板: 大港区| 东兰县| 万盛区| 阳春市| 斗六市| 长汀县| 潮州市| 彭州市| 鲁甸县| 兴和县| 哈尔滨市| 九江县| 泰兴市| 大庆市| 双鸭山市| 德兴市| 霸州市| 石家庄市| 康马县| 简阳市| 察雅县| 河北省| 黑龙江省| 喜德县| 郎溪县| 日喀则市| 苏尼特右旗| 天镇县| 随州市| 尼玛县| 达孜县| 广丰县| 本溪| 邓州市| 阜新| 灯塔市| 东阳市| 泾川县| 徐汇区| 新蔡县| 香格里拉县|