美文网首页
线程基础示例代码之ReaderWriterLockSlim

线程基础示例代码之ReaderWriterLockSlim

作者: JameLee | 来源:发表于2018-06-26 23:06 被阅读0次

    ReaderWriterLockSlim 类可用于多个线程同时读,但同一时间,只能有一个线程写的情况

    ReaderWriterLockSlim 类支持三种锁定模式:ReadWriteUpgradeableRead。这三种模式对应的方法分别为

    • 进入:EnterReadLock/TryEnterReadLockEnterWriteLock/TryEnterWriteLockEnterUpgradeableReadLock/TryEnterUpgradeableReadLock
    • 退出:ExitReadLockExitWriteLockExitUpgradeableReadLock

    ReaderWriterLockSlim 类似于 ReaderWriterLock, 只是简化了递归、升级和降级锁定状态的规则。ReaderWriterLockSlim可避免很多潜在的可能会产生死锁的情况。此外,ReaderWriterLockSlim 性能明显优于 ReaderWriterLock

    需要注意的点

    • 在使用完 ReaderWriterLockSlim 对象之后,我们需要释放该对象拥有的资源(使用using或手动调用Dispose方法)
    • 尽可能的不使用递归策略,因为这种方式导致死锁的概率非常高

    一个 ReaderWriterLockSlim 可以是以下四种状态之一

    • Not Entered : 表示没有线程进入这个锁(也有可能所有线程都已经退出了)
    • Read :表示一个或多个用于读资源的线程进入了锁。一个线程可以通过 EnterReadLockTryEnterReadLock 方法进入读模式
    • Upgrade :此状态下,一次只能有一个线程可以升级为写模式,其他希望升级的线程将会被阻塞
    • Write :表示一个具有写入权限的线程进入了锁。其他希望进入写入权限的线程将会被阻塞

    示例代码如下

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    public class App {
        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 {
                    if (innerCache.TryGetValue(key, out string 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();
            }
        }
    
        public static void Main() {
            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;
                Console.WriteLine("Task {0} wrote {1} items\n", 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));
    
                        Console.WriteLine("Task {0} read {1} items: {2}\n", 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)
                            Console.WriteLine("Changed 'cucumber' to 'green bean'");
                }
            }));
    
            // Wait for all three tasks to complete.
            Task.WaitAll(tasks.ToArray());
    
            // Display the final contents of the cache.
            Console.WriteLine();
            Console.WriteLine("Values in synchronized cache: ");
            for (int ctr = 1; ctr <= sc.Count; ctr++)
                Console.WriteLine("   {0}: {1}", ctr, sc.Read(ctr));
        }
    }
    

    在实际项目中,以上的 SynchronizedCache 类完全可以使用 ConcurrentDictionary<int, string> 来替代



    至此,本节内容讲解完毕。
    欢迎关注公众号【嘿嘿的学习日记】,所有的文章,都会在公众号首发,Thank you~

    公众号二维码.jpg

    相关文章

      网友评论

          本文标题:线程基础示例代码之ReaderWriterLockSlim

          本文链接:https://www.haomeiwen.com/subject/wdjvyftx.html