C# 多线程锁之ReaderWriterLockSlim

1、简介

.NET 3.5 开始 ReaderWriterLockSlim登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLock 的升级版。 由于 ReaderWriterLockSlim 默认不支持递归调用、所以在某种意义上来说更不容易造成死锁。
ReaderWriterLockSlim 类支持三种锁定模式:Read,Write,UpgradeableRead。这三种模式对应的方法分别是 EnterReadLock,EnterWriteLock,EnterUpgradeableReadLock 。再就是与此对应的 TryEnterReadLock,TryEnterWriteLock,TryEnterUpgradeableReadLock,ExitReadLock,ExitWriteLock,ExitUpgradeableReadLock。Read 和 Writer 锁定模式比较简单易懂:Read 模式是典型的共享锁定模式,任意数量的线程都可以在该模式下同时获得锁;Writer 模式则是互斥模式,在该模式下只允许一个线程进入该锁。UpgradeableRead 锁定模式可能对于大多数人来说比较新鲜,但是在数据库领域却众所周知。

1、对于同一把锁、多个线程可同时进入读模式。
2、对于同一把锁、同时只允许一个线程进入写模式。
3、对于同一把锁、同时只允许一个线程进入可升级的读模式。
4、通过默认构造函数创建的读写锁是不支持递归的,若想支持递归 可通过构造 ReaderWriterLockSlim(LockRecursionPolicy) 创建实例。
5、对于同一把锁、同一线程不可两次进入同一锁状态(开启递归后可以)
6、对于同一把锁、即便开启了递归、也不可以在进入读模式后再次进入写模式或者可升级的读模式(在这之前必须退出读模式)。
7、再次强调、不建议启用递归。
8、读写锁具有线程关联性,即两个线程间拥有的锁的状态相互独立不受影响、并且不能相互修改其锁的状态。
9、升级状态:在进入可升级的读模式 EnterUpgradeableReadLock后,可在恰当时间点通过EnterWriteLock进入写模式。
10、降级状态:可升级的读模式可以降级为读模式:即在进入可升级的读模式EnterUpgradeableReadLock后, 通过首先调用读取模式EnterReadLock方法,然后再调用 ExitUpgradeableReadLock 方法。

这段简介来自https://www.cnblogs.com/majiang/p/8133979.html,来自一个前辈的文章,总结的很好,而且有源码解析,有兴趣的可以观看,通过这段话结合MSDN关于ReaderWriterLockSlim的介绍,能大致得知道ReaderWriterLockSlim得用处,在多线程并发操作共享资源时,很有用处.

2、通过ReaderWriterLockSlim封装一个同步缓存实例

下面时MS提供的封装,我做了略微的修改,添加了一些注释,使API更能看懂,代码如下:

 public class SynchronizedCache
    {
        private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();

        /// <summary>
        /// 同步缓存块维护的数据资源
        /// </summary>
        private Dictionary<int, string> innerCache = new Dictionary<int, string>();

        /// <summary>
        /// 同步缓存块维护的数据资源长度
        /// </summary>
        public int Count
        {
            get { return innerCache.Count; }
        }

        /// <summary>
        /// 线程安全的添加操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Add(int key,string value)
        {
            //尝试进入写入模式锁定状态
            cacheLock.EnterWriteLock();
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                //退出写入模式锁定状态
                cacheLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// 带锁超时的添加的操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        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;
            }
        }

        /// <summary>
        /// 线程安全的读取操作
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string Read(int key)
        {
            cacheLock.EnterReadLock();
            try
            {
                return innerCache[key];
            }
            finally
            {
                cacheLock.ExitReadLock();
            }
        }

        /// <summary>
        /// 线程安全的添加修改操作
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        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();
            }
        }

        /// <summary>
        /// 线程安全的删除操作
        /// </summary>
        /// <param name="key"></param>
        public void Delete(int key)
        {
            cacheLock.EnterWriteLock();
            try
            {
                innerCache.Remove(key);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// 添加或修改时产生的状态
        /// </summary>
        public enum AddOrUpdateStatus
        {
            Added,
            Updated,
            Unchanged
        };

        /// <summary>
        /// 析构 释放资源
        /// </summary>
        ~SynchronizedCache()
        {
            if (cacheLock != null) cacheLock.Dispose();
        }
    }

下面时使用案列代码如下:

  var lockCache = new SynchronizedCache();
            var tasks = new List<Task>();//模拟线程集合

            //注入写入内容线程
            tasks.Add(Task.Run(() => {
                var list = new List<string> {"钟","声","响","起","归","家","的","讯","号"};
                var listCount = list.Count;
                for (var i = 0; i < listCount; i++)
                {
                    lockCache.Add(i, list[i]);
                }
                Console.WriteLine($"Task {Task.CurrentId} wrote {listCount} items\n");
            }));

            //注入两个读线程,一个正向遍历同步缓存块维护的数据资源一个逆向遍历同步缓存块维护的数据资源
            //由于读线程可能在写线程之前执行,所以输入内容时可能为空
            for (var i = 0; i <= 1; i++)
            {
                var flag = Convert.ToBoolean(i);
                tasks.Add(Task.Run(() =>
                {
                    int startIndex, lastIndex, step;//开始、结束索引、递增指数
                    string outPut=string.Empty;//输出
                    int items;//线程执行顺序可能不同,所以个参数用于判断在执行读取操作时,上面的写入线程是否执行完毕
                    do
                    {
                        items = lockCache.Count;
                        //正向遍历
                        if (!flag)
                        {
                            startIndex = 0;
                            lastIndex = items;
                            step = 1;
                        }
                        //反向遍历
                        else
                        {
                            startIndex = items - 1;
                            lastIndex = 0;
                            step = -1;
                        }
                        for (var j = startIndex; flag ? j >= lastIndex : j < lastIndex; j += step)
                        {
                            outPut += $"{lockCache.Read(j)} ";
                        }
                        Console.WriteLine($"Task {Task.CurrentId} read {items} items: {outPut}\n");
                    } while (lockCache.Count == 0 | items< lockCache.Count);
                }));
            }

            //注入一个线程去修改数据
            tasks.Add(Task.Run(() => {
                Thread.Sleep(100);//强制当前线程休息,防止写入数据线程还没有执行完毕,就去更新了数据
                for (int ctr =0; ctr < lockCache.Count; ctr++)
                {
                    string value = lockCache.Read(ctr);
                    if (value == "家")
                        if (lockCache.AddOrUpdate(ctr, "Home") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
                            Console.WriteLine("Changed ‘家‘ to ‘Home‘");
                }
            }));

            Task.WhenAll(tasks).ContinueWith(task =>
            {
                Console.WriteLine();
                Console.WriteLine("Values in synchronized cache: ");
                for (int ctr = 0; ctr < lockCache.Count; ctr++)
                    Console.WriteLine("   {0}: {1}", ctr, lockCache.Read(ctr));
            });

            Console.ReadKey();

调用完毕,有点ConncurrentDictionary的味道,还没看它的代码,接下去的随笔会分析,对比下两种方式的差距.

原文地址:https://www.cnblogs.com/GreenLeaves/p/10618613.html

时间: 2024-08-30 11:02:28

C# 多线程锁之ReaderWriterLockSlim的相关文章

synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解

本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁,源码剖析 第一部分:synchronized与static synchronized的差别 1.synchronized与static synchronized 的差别 synchronized是对类的当前实例进行加锁,防止其它线程同一时候訪问该类的该实例的全部synchronized块.注意这里

Python多线程锁

[Python之旅]第六篇(四):Python多线程锁 python lock 多线程 多线程使用方法 多线程锁 摘要:   在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来说一说. 1.给线程加锁的原因     我们知道,不同进程之间的内存空间数据是不能够共享的,试想一下,如果可以随意共享,谈何安全?但是一个进程中的多个线程是可以共享这个进程的内存空间中的数据的,比如多个线程可以同时调用某一... 在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来说一

synchronized与static synchronized 的区别、synchronized在JVM底层的实现原理及Java多线程锁理解

本Blog分为如下部分: 第一部分:synchronized与static synchronized 的区别 第二部分:JVM底层又是如何实现synchronized的 第三部分:Java多线程锁,源代码剖析 第一部分:synchronized与static synchronized的区别 1.synchronized与static synchronized 的区别 synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是"类

【Python之旅】第六篇(四):Python多线程锁

    在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来说一说. 1.给线程加锁的原因 我们知道,不同进程之间的内存空间数据是不能够共享的,试想一下,如果可以随意共享,谈何安全?但是一个进程中的多个线程是可以共享这个进程的内存空间中的数据的,比如多个线程可以同时调用某一内存空间中的某些数据(只是调用,没有做修改). 试想一下,在某一进程中,内存空间中存有一个变量对象的值为num=8,假如某一时刻有多个线程需要同时使用这个对象,出于这些线程要实现不同功能的需要,线程A需要将n

python:线程,多线程锁,多线程递归锁

#!usr/bin/env python# -*- coding:utf-8 -*- __author__ = "Samson" import threading,timedef run(n): print("task", n) time.sleep(2) print("current thread:",threading.current_thread())#当前线程 t_obj = []#存线程实例start_time = time.time(

c++多线程——锁技巧

[转自]here 编写程序不容易,编写多线程的程序更不容易.相信编写过多线程的程序都应该有这样的一个痛苦过程,什么样的情况呢?朋友们应该看一下代码就明白了, void data_process() { EnterCriticalSection(); if(/* error happens */) { LeaveCriticalSection(); return; } if(/* other error happens */) { return; } LeaveCriticalSection();

.NET 同步与异步之锁(ReaderWriterLockSlim)(八)

本随笔续接:.NET 同步与异步之锁(Lock.Monitor)(七) 由于锁 ( lock 和 Monitor ) 是线程独占式访问的,所以其对性能的影响还是蛮大的,那有没有一种方式可是实现:允许多个线程同时读数据.只允许一个线程写数据呢?答案是肯定的. 读写锁 ReaderWriterLock .就是 支持单个写线程和多个读线程的锁.自.NET 3.5 开始 ReaderWriterLockSlim.登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLo

java多线程--“锁”总览

根据锁的添加到Java中的时间,Java中的锁,可以分为"同步锁"和"JUC包中的锁". 同步锁 即通过synchronized关键字来进行同步,实现对竞争资源的互斥访问的锁.Java 1.0版本中就已经支持同步锁了. 同步锁的原理是,对于每一个对象,有且仅有一个同步锁:不同的线程能共同访问该同步锁.但是,在同一个时间点,该同步锁能且只能被一个线程获取到.这样,获取到同步锁的线程就能进行CPU调度,从而在CPU上执行:而没有获取到同步锁的线程,必须进行等待,直到获取

python 多线程锁机制

GIL(全局解释器锁) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的. GIL原理图 计算密集型:结果肯定是100,因为每一次start结果就已经出来了,所以第二个线程肯