深入多线程之:Reader与Write Locks(读写锁)的使用详解

所属分类: 软件编程 / C#教程 阅读数: 67
收藏 0 赞 0 分享

线程安全的一个很经常的需求是允许并发读,但是不允许并发写,例如对于文件就是这样的。

ReaderWriterLockSlim 在.net framework 3.5的时候就提供了,它是用来代替以前的”fat”版本的”ReaderWriterLock”

这两个类,有两种基本的锁----一个读锁,一个写锁。

写锁是一个完全排他锁。

读锁可以和其他的读锁兼容


因此当一个线程持有写锁的是很,所有的尝试获取读锁和写锁的线程全部阻塞,但是如果没有一个线程持有写锁,那么可以有一系列的线程并发的获取读锁。

ReaderWriterLockSlim 定义了下面几个方法来获取和释放 读写锁。

    Public void EnterReadLock();
    Public void ExitReadLock();
    Public void EnterWriteLock();
    Public void ExitWriteLock();

和Monitor.TryEnter类似,ReaderWriterLockSlim 再对应的”EnterXXX”方法上也提供了相应的”Try”版本。ReaderWriterLock提供了AcquireXXX 和 ReleaseXXX 方法,当超时发生了,ReaderWriterLock 抛出一个ApplicationException,而不是返回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 来确保即使异常抛出了,锁也被正确的释放了。

CurrentReadCount 属性,ReaderWriterLockSlim 提供了以下属性用来监视锁。

可更新锁:

再一个原子操作里将读锁升级为写锁是很有用的,例如,假设你想要再一个list 里面写一些不存在的项的时候, 你可能会执行下面的一些步骤:

    获取一个读锁。
    测试,如果要写的东西在列表中,那么释放锁,然后返回。
    释放读锁。
    获取一个写锁
    添加项,写东西,
    释放写锁。

问题是:在第三步和第四步之间,可能有另一个线程修改了列表。

ReaderWriterLockSlim 通过一个叫做可更新锁( upgradeable lock),来解决这个问题。

一个可更新锁除了它可以在一个原子操作中变成写锁外很像一个读锁,你可以这样使用它:

    调用EnterUpgradeableReadLock 获取可更新锁。执行一些读操作,例如判断要写的东西在不在List中。调用EnterWriteLock , 这个方法会将可更新锁 升级为 写锁。执行写操作,调用ExitWriteLock 方法,这个方法将写锁转换回可更新锁。继续执行一些读操作,或什么都不做。
    调用ExitUpgradeableReadLock 释放可更新锁。

从调用者的角度来看,它很像一个嵌套/递归锁,从功能上讲,在第三步,

ReaderWriterLockSlim 在一个原子操作里面释放读锁,然后获取写锁。

可更新锁和读锁的重要区别是:尽管可更新锁可以和读锁共存,但是一次只能有一个可更新锁被获取。这样的主要目的是防止死锁。

这样我们可以修改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#提供的读写锁功能强大,使用方便,

所以在自己编写读写锁的时候,要考虑下是否需要支持可更新锁,是否有必要自己写一个读写锁.

更多精彩内容其他人还在看

WPF仿三星手机充电界面实现代码

这篇文章主要为大家详细介绍了WPF仿三星手机充电界面实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

浅谈C#各种数组直接的数据复制/转换

下面小编就为大家带来一篇浅谈C#各种数组直接的数据复制/转换。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

C#访问SQLServer增删改查代码实例

这篇文章主要为大家详细介绍了C#访问SQLServer增删改查代码实例,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

C#根据身份证号码判断出生日期和性别

这篇文章主要为大家详细介绍了C#根据身份证号码判断出生日期和性别的方法,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

C# 向Word中设置/更改文本方向的方法(两种)

在一般情况下word中输入的文字都是横向的,今天小编给大家带来两种方法来设置更改文本方向的方法,非常不错,对c# word 更改文本方向的知识感兴趣的朋友一起看看吧
收藏 0 赞 0 分享

让C# Excel导入导出 支持不同版本Office

让C# Excel导入导出,支持不同版本的Office,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

C#设置自定义文件图标实现双击启动(修改注册表)

这篇文章介绍的是利用C#设置自定义文件图标,然后实现双击启动的功能,文章给出了示例代码,介绍的很详细,有需要的可以参考借鉴。
收藏 0 赞 0 分享

C#两个相同属性的类赋值方法

这篇文章主要介绍了C#两个相同属性的类赋值方法的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

C#中ListView控件实现窗体代码

这篇文章主要介绍了C#中ListView控件实现窗体的核心代码,非常不错,具有参考借鉴价值,对c#listview相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

浅谈C# 序列化与反序列化几种格式的转换

下面小编就为大家带来一篇浅谈C# 序列化与反序列化几种格式的转换。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享
查看更多