异步多线程 3 多线程安全

多线程去访问同一个集合,一般没问题,线程安全问题一般是出在同时修改一个对象的时候。

线程安全问题:一段代码,单线程执行和多线程执行,结果不一致。

例如这个方法,很简单,循环开启task往一个list里add,等3秒钟执行完后打印出来List的长度,如果是单线程,肯定是10000,现在看看多线程的结果。

        private static void AsyncSafeMethod()
        {
            List<int> listInt = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                Task.Run(() => {
                    listInt.Add(i);
                });
            }
            Thread.Sleep(3000);
            Console.WriteLine(listInt.Count);
        }

![]()

而且基本上每次执行的结果都不一样,这说明,出现了多线程安全的问题,因为当多个线程会出现同时对list的同一个下标进行add的操作,导致值的覆盖,也就是同时往内存的同一个地址进行了多次赋值,导致先赋值进去的丢失。

最常用的解决方案是 Lock

Lock是Monitor的语法糖,它其实是锁住了一个内存的引用地址,所以Lock的对象肯定不能是值类型,因为值类型就保存在栈中,根本就没有向堆引用,那怎么锁,另外null也不能锁。

那么当多线程执行到Lock这里时,第一个到达的线程会占据这个lock_obj的引用,也就是加了个状态,当别的线程企图访问这个代码块前,需要先看看这个lock_obj的状态,发现被占据了,就会等待至占据的线程执行完代码块并重置这个状态。至于会不会多个线程同时占据引用,目前是不会,因为这是原子级别的操作,想一想事务的原子性的概念,不可分割,大概是这个意思。

声明一个object对象,用lock锁住Add这一段

        /// <summary>
        /// 锁对象,官方标准推荐写法。
        /// </summary>
        private static readonly object lock_Obj = new object();
        private static void AsyncSafeMethod()
        {
            List<int> listInt = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                Task.Run(() =>
                {
                    lock (lock_Obj)
                    {
                        listInt.Add(i);
                    }
                });
            }
            Thread.Sleep(3000);
            Console.WriteLine(listInt.Count);
        }

结果

![]()

这里说一下为什么是锁变量推荐 private static readonly

私有是防止有其他代码方法这个锁变量,自己的锁就自己用,否则有可能形成不同的功能之间的互相竞争,功能之间无法并发,形成阻塞。

静态是确保不同的实例调用的是同一个锁变量,如果不是静态,在一段代码中实例化多次这个类的对象时,每次的锁都是新的,这样多次实例化的对象调用加锁的方法,它们之间会形成并发。

只读,如果不是只读,有人把锁变量重新改了,那之间的锁就失效了,因为引用已经变了。

为啥不能用String当锁变量呢?因为String类型比较特殊,多个string如果赋值相同的话,不会被视为不同的对象,而是通过同样的引用指向同一个堆的地址,这样你用这多个string当锁变量的话,互相之间会竞争,无法并发。

使用Lock,其实就是保证被它包起来的代码块,同一时刻只能有一个线程访问,其他要排队,说白了,就是单线程化。

声明:该文章系转载,转载该文章的目的在于更广泛的传递信息,并不代表本网站赞同其观点,文章内容仅供参考。

本站是一个个人学习和交流平台,网站上部分文章为网站管理员和网友从相关媒体转载而来,并不用于任何商业目的,内容为作者个人观点, 并不代表本网站赞同其观点和对其真实性负责。

我们已经尽可能的对作者和来源进行了通告,但是可能由于能力有限或疏忽,导致作者和来源有误,亦可能您并不期望您的作品在我们的网站上发布。我们为这些问题向您致歉,如果您在我站上发现此类问题,请及时联系我们,我们将根据您的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。