zoukankan      html  css  js  c++  java
  • .NET 同步与异步之锁(ReaderWriterLockSlim)(八)

    本随笔续接:.NET 同步与异步之锁(Lock、Monitor)(七)

    由于锁 ( lock 和 Monitor ) 是线程独占式访问的,所以其对性能的影响还是蛮大的,那有没有一种方式可是实现:允许多个线程同时读数据、只允许一个线程写数据呢?答案是肯定的。

    读写锁 ReaderWriterLock 、就是 支持单个写线程和多个读线程的锁。自.NET 3.5 开始 ReaderWriterLockSlim 、登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLock 的升级版。 由于 ReaderWriterLockSlim  默认不支持递归调用、所以在某种意义上来说更不容易造成死锁。

    一、先看一下demo(来源msdn代码示例):

        public class SynchronizedCache
        {
            private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
            private Dictionary<int, string> innerCache = new Dictionary<int, string>();
    
            public int Count
            { get { return innerCache.Count; } }
    
            public string Read(int key)
            {
                cacheLock.EnterReadLock();
                try
                {
                    return innerCache[key];
                }
                finally
                {
                    cacheLock.ExitReadLock();
                }
            }
    
            public void Add(int key, string value)
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache.Add(key, value);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
            }
    
            public bool AddWithTimeout(int key, string value, int timeout)
            {
                if (cacheLock.TryEnterWriteLock(timeout))
                {
                    try
                    {
                        innerCache.Add(key, value);
                    }
                    finally
                    {
                        cacheLock.ExitWriteLock();
                    }
                    return true;
                }
                else
                {
                    return false;
                }
            }
    
            public AddOrUpdateStatus AddOrUpdate(int key, string value)
            {
                cacheLock.EnterUpgradeableReadLock();
                try
                {
                    string result = null;
                    if (innerCache.TryGetValue(key, out result))
                    {
                        if (result == value)
                        {
                            return AddOrUpdateStatus.Unchanged;
                        }
                        else
                        {
                            cacheLock.EnterWriteLock();
                            try
                            {
                                innerCache[key] = value;
                            }
                            finally
                            {
                                cacheLock.ExitWriteLock();
                            }
                            return AddOrUpdateStatus.Updated;
                        }
                    }
                    else
                    {
                        cacheLock.EnterWriteLock();
                        try
                        {
                            innerCache.Add(key, value);
                        }
                        finally
                        {
                            cacheLock.ExitWriteLock();
                        }
                        return AddOrUpdateStatus.Added;
                    }
                }
                finally
                {
                    cacheLock.ExitUpgradeableReadLock();
                }
            }
    
            public void Delete(int key)
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache.Remove(key);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
            }
    
            public enum AddOrUpdateStatus
            {
                Added,
                Updated,
                Unchanged
            };
    
            ~SynchronizedCache()
            {
                if (cacheLock != null) cacheLock.Dispose();
            }
        }
    
    
            private void ReaderWriterLock()
            {
                var sc = new SynchronizedCache();
                var tasks = new List<Task>();
                int itemsWritten = 0;
    
                // Execute a writer.
                tasks.Add(Task.Run(() =>
                {
                    String[] vegetables = { "broccoli", "cauliflower",
                                                              "carrot", "sorrel", "baby turnip",
                                                              "beet", "brussel sprout",
                                                              "cabbage", "plantain",
                                                              "spinach", "grape leaves",
                                                              "lime leaves", "corn",
                                                              "radish", "cucumber",
                                                              "raddichio", "lima beans" };
                    for (int ctr = 1; ctr <= vegetables.Length; ctr++)
                        sc.Add(ctr, vegetables[ctr - 1]);
    
                    itemsWritten = vegetables.Length;
    
                    base.PrintInfo(string.Format("Task {0} wrote {1} items
    ", Task.CurrentId, itemsWritten));
                }));
                // Execute two readers, one to read from first to last and the second from last to first.
                for (int ctr = 0; ctr <= 1; ctr++)
                {
                    bool desc = Convert.ToBoolean(ctr);
                    tasks.Add(Task.Run(() =>
                    {
                        int start, last, step;
                        int items;
                        do
                        {
                            String output = String.Empty;
                            items = sc.Count;
                            if (!desc)
                            {
                                start = 1;
                                step = 1;
                                last = items;
                            }
                            else
                            {
                                start = items;
                                step = -1;
                                last = 1;
                            }
    
                            for (int index = start; desc ? index >= last : index <= last; index += step)
                                output += String.Format("[{0}] ", sc.Read(index));
    
                            base.PrintInfo(string.Format("Task {0} read {1} items: {2}
    ", Task.CurrentId, items, output));
    
                        } while (items < itemsWritten | itemsWritten == 0);
                    }));
                }
                // Execute a red/update task.
                tasks.Add(Task.Run(() =>
                {
                    Thread.Sleep(100);
                    for (int ctr = 1; ctr <= sc.Count; ctr++)
                    {
                        String value = sc.Read(ctr);
                        if (value == "cucumber")
                            if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
                                base.PrintInfo("Changed 'cucumber' to 'green bean'");
                    }
                }));
    
                // Wait for all three tasks to complete.
                Task.WaitAll(tasks.ToArray());
    
                // Display the final contents of the cache.
                base.PrintInfo("");
                base.PrintInfo("Values in synchronized cache: ");
                for (int ctr = 1; ctr <= sc.Count; ctr++)
                    base.PrintInfo(string.Format("   {0}: {1}", ctr, sc.Read(ctr)));
            }
    Demo

    二、通过Demo我们来看一下 ReaderWriterLockSlim  的用法:

    1、EnterWriteLock   进入写模式锁定状态

    2、EnterReadLock    进入读模式锁定状态

    3、EnterUpgradeableReadLock  进入可升级的读模式锁定状态

    并且三种锁定模式都有超时机制、对应 Try... 方法,退出相应的模式则使用 Exit... 方法,而且所有的方法都必须是成对出现的。

    三、备注及注意事项

    1、对于同一把锁、多个线程可同时进入 读模式。

    2、对于同一把锁、同时只允许一个线程进入 写模式。

    3、对于同一把锁、同时只允许一个线程进入 可升级的读模式。

    4、通过默认构造函数创建的读写锁是不支持递归的,若想支持递归 可通过构造 ReaderWriterLockSlim(LockRecursionPolicy) 创建实例。

    5、对于同一把锁、同一线程不可两次进入同一锁状态(开启递归后可以)

    6、对于同一把锁、即便开启了递归、也不可以在进入读模式后再次进入写模式或者可升级的读模式(在这之前必须退出读模式)。

    7、再次强调、不建议启用递归。

    8、读写锁具有线程关联性,即 两个线程间拥有的锁的状态 相互独立不受影响、并且不能相互修改其锁的状态。

    9、升级状态:在进入可升级的读模式 EnterUpgradeableReadLock  后,可在恰当时间点 通过 EnterWriteLock   进入写模式。

    10、降级状态:可升级的读模式可以降级为读模式:即 在进入可升级的读模式 EnterUpgradeableReadLock  后, 通过首先调用读取模式 EnterReadLock 方法,然后再调用 ExitUpgradeableReadLock 方法。 

    随笔暂告一段落、下一篇随笔介绍:轻量级的锁(Interlocked、SpinLock)(预计1篇随笔)

    附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

    参见更多:随笔导读:同步与异步


    (未完待续...)

    转载于:https://www.cnblogs.com/08shiyan/p/6423532.html

  • 相关阅读:
    js 变量提升和函数提升原理
    解析PHP中intval()等int转换时的意外异常情况
    不要太相信自己的眼睛
    遇到乱码时的一些想法
    c++ --> 变量、常量与运算符
    [ActionScript3.0] 逻辑或"||=" ,等于"=="和全等于"==="
    [ActionScript3.0] 传递任意数量的参数
    [ActionScript3.0] 深表复制
    [ActionScript3.0] 为内建类添加方法
    Jmeter之内存溢出解决办法
  • 原文地址:https://www.cnblogs.com/twodog/p/12141215.html
Copyright © 2011-2022 走看看