前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# 温故而知新: 线程篇(三)下

C# 温故而知新: 线程篇(三)下

作者头像
逸鹏
发布2018-04-10 15:21:03
6170
发布2018-04-10 15:21:03
举报
文章被收录于专栏:逸鹏说道逸鹏说道

结果计时器会一直滚动,因为a对象被锁住,除非完成Thread.Sleep(3000000)后才能进入到a共享区

由于以上的问题,微软还是建议我们使用一个私有的变量来锁定,由于私有变量外界无法访问,所以锁住话死锁的可能性大大下降了。

这样我们就能选择正确的“门”来进行锁住,但是可能还有一种可能也会造成死锁,就是在lock内部出现了问题,由于死锁非常复杂,我将在

今后的文章中专门写一篇关于死锁的文章来深入解释下死锁,所以这里就对死锁不深究了,这里大伙了解下lock的使用方法和注意事项就行了。

5.ReaderWriterLock

由于lock关键字对临界区(共享区)保护的非常周密,导致了一些功能可能会无法实现,假设我将某个查询功能放置在临界区中时,可能

当别的线程在查询临界区中的数据时,可能我的那个线程被阻塞了,所以我们期望锁能够达到以下功能

1 首先锁能细分为读锁和写锁

2 能够保证同时可以让多个线程读取数据

3 能保证同一时刻只有一个线程能进行写操作,也就是说,对于写操作,它必须拥有独占锁

4 能保证一个线程同一时刻只能拥有写锁或读锁中的一个

显然lock关键字无法满足我们的需求,还好微软想到了这点,ReaderWriterLock便隆重登场了ReaderWriterLock能够达到的效果是:

1. 同一时刻,它允许多个读线程同时访问临界区,或者允许单个线程进行写访问

2. 在读访问率很高,而且写访问率很低的情况下,效率最高,

3.它也满足了同一时刻只能获取写锁或读锁的要求。

4. 最为关键的是,ReaderWriterLock能够保证读线程锁和写线程锁在各自的读写队列中,当某个线程释放了写锁了,同时读线程队列中

的所有线程将被授予读锁,同样,当所有的读锁被释放时,写线程队列中的排队的下一个线程将被授予写锁,更直观的说,ReaderWriterLock

就是在这几种状态间来回切换

5 使用时注意每当你使用AcquireXXX方法获取锁时,必须使用ReleaseXXX方法来释放锁

6 ReaderWriterLock 支持递归锁,关于递归锁会在今后的章节详细阐述

7 在性能方面ReaderWriterLock做的不够理想,和lock比较差距明显,而且该类库中还隐藏些bug,有于这些原因,微软又专门重新写了个新

ReaderWriterLockSilm来弥补这些缺陷。

8 处理死锁方面ReaderWriterLock为我们提供了超时的参数这样我们便可以有效的防止死锁

9 对于一个个获取了读锁的线程来说,在写锁空闲的情况下可以升级为写锁

接着让我们了解下ReaderWriterLock的重要成员

上述4个方法分别是让线程获取写锁和读锁的方法,它利用的计数的概念,当一个线程中调用此方法后,该类会给该线程拥有的锁计数加1

(每次加1,但是一个线程可以拥有多个读锁,所以计数值可能更多,但是对于写锁来说同时一个一个线程可以拥有)。后面的参数是超时

时间,我们可以自己设置来避免死锁。同样调用上述方法后我们必须使用ReleaseXXX 方法来让计数值减1,直到该线程拥有锁的计数为0,

释放了锁为止。

最后我们用一个简单的例子来温故下上述的知识点(请注意看注释)

代码语言:javascript
复制
/// <summary>
    /// 该示例通过ReaderWriterLock同步来实现Student集合多线程下
    /// 的写操作和读操作
    /// </summary>
    class Program
    {
        static ReaderWriterLock _readAndWriteLock = new ReaderWriterLock();
        static List<Student> demoList = new List<Student>();


        static void Main(string[] args)
        {
            InitialStudentList();
            Thread thread=null;
            for (int i = 0; i <5; i++)
            {
 
                //让第前2个个线程试图掌控写锁,
                if (i < 2)
                {
                    thread = new Thread(new ParameterizedThreadStart(AddStudent));
                    Console.WriteLine("线程ID:{0}, 尝试获取写锁        ", thread.ManagedThreadId);
                    thread.Start(new Student { Name = "Zhang" + i });
                }
                else 
                {
                    //让每个线程都能访问DisplayStudent 方法去获取读锁
                    thread = new Thread(new ThreadStart(DisplayStudent));
                    thread.Start();
                }
                Thread.Sleep(20);
            }
            Console.ReadKey();
        }


        static void InitialStudentList() 
        {
            demoList = new List<Student> { new Student{ Name="Sun"}, new Student{Name="Zheng"} };
        }


        /// <summary>
        /// 当多个线程试图使用该方法时,只有一个线程能够透过AcquireSWriterLock
        /// 获取写锁,同时其他线程进入队列中等待,直到该线程使用ReleaseWriterLock后
        /// 下个线程才能进入拥有写锁
        /// </summary>
        /// <param name="student"></param>
        static void AddStudent(object student)
        {
            if (student == null|| !(student is Student)) return;
            if (demoList.Contains(student)) return;
            try
            {
                //获取写锁
                _readAndWriteLock.AcquireWriterLock(Timeout.Infinite);
                demoList.Add(student as Student);
                Console.WriteLine("当前写操作线程为{0},            写入的学生是:{1}", Thread.CurrentThread.ManagedThreadId,(student as Student).Name);
            }
            catch (Exception)
            {


            }
            finally
            {
                _readAndWriteLock.ReleaseWriterLock();
            }


        }


        /// <summary>
        /// 对于读锁来所,允许多个线程共同拥有,所以这里同时
        /// 可能会有多个线程访问Student集合,使用try catch是为了
        /// 一定要让程序执行finally语句块中的releaseXXX方法,从而保证
        /// 能够释放锁
        /// </summary>
        static void DisplayStudent()
        {
            try
            {
                _readAndWriteLock.AcquireReaderLock(Timeout.Infinite);
                demoList.ForEach(student
                  =>
                {
                    Console.WriteLine("当前集合中学生为:{0},当前读操作线程为{1}", student.Name, Thread.CurrentThread.ManagedThreadId);
                });
            }
            catch (Exception)
            {
 
            }
            finally
            {
                _readAndWriteLock.ReleaseReaderLock();
            }
        }


    }


    internal class Student
    {
        public string Name { get; set; }
    }

运行结果:

从例子可以看出有2个线程试图尝试争取写锁,但是同时只有一个线程可以获取到写锁,同时对于读取集合的线程可以同时获取多个读锁

6. 本章总结

由于本人上个月工作突然忙了起来,快一个多月没更新博客了,希望大家可以见谅^^

本章介绍了线程同步的概念和一些关于同步非常重要的基本概念,对于原子性的操作的认识也格外重要,同时对于Volatile,Interlocked,lock,ReaderWriterLock 知识点做了相关介绍,

相信大家对于线程同步有个初步的认识和理解,在写本篇博客时,发现死锁也是个很重要的知识点,关于死锁我会单独写篇文章来阐述,谢谢大家的支持!

7. 参考资料

CLR via c#

msdn

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 我为Net狂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档