C#关于List的线程安全问题(二)

上一期,C#关于List的线程安全问题(一)我们给出了一个线程不安全的例子。

这个例子给人的感觉就是总觉得哪里不对,命名插入5000个数据到List中,结果却并不是自己想要的。

明明一共插入了1300个数据,结果也不是。

这都是因为List默认线程不安全导致的,也就是当某一个线程正在往List中插入数据,结果由于其他线程也正在做插入动作,导致冲突,插入可能失败,并且插入的顺序是不可控的,除非我们并不关心插入的顺序。

那么我们怎么样才能让结果是我们所需要的呢?

这里我们引入ICollection接口(List<T>是拥有ICollection接口的),这个接口当中有个叫SyncRoot的属性,它是个辅助属性,它是微软实现为我们准备好了拿来做同步用的,也就是拿来做线程安全用的。它一般用于lock语句块。用来将集合资源锁定,可用于同步对ICollection的访问的对象。

public object SyncRoot { get; }

它是一个实例化的属性,即每个List实例对象都会有各自的一个SyncRoot。如果想安全访问集合对象,我们可以如下使用:

ICollection myCollection = someCollection;
lock(myCollection.SyncRoot)
{
    // Some operation on the collection, which is now thread safe.
}

所以C#关于List的线程安全问题(一)中的例子,我们可以稍加修改如下:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;

namespace vscode_test2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> mylist = new List<int>();
            var t = Task.Run(()=>{
                Thread.Sleep(2000);
                lock((mylist as ICollection).SyncRoot)
                {
                    for(int i=0; i<8000; i++)
                    {
                        mylist.Add(3);
                        Thread.Sleep(1);
                    }
                    System.Console.WriteLine($"task: list size:{mylist.Count}");
                }
            });
            Thread.Sleep(2000);
            lock((mylist as ICollection).SyncRoot)
            {
                for(int i=0; i<5000; i++)
                {
                    Thread.Sleep(1);
                    mylist.Add(6);
                }
                System.Console.WriteLine($"main: list size:{mylist.Count}");
            }
            t.Wait();
            Console.Read();
        }
    }
}

/*
main: list size:5000
task: list size:13000
*/

这是输出结果,就是我们想要的。当然这个想要的,是如您业务所愿。如果你是无所谓插入顺序或者插入是否失败,那么代码随你写。因此,安不安全,同步与否是需要有业务逻辑来定的。反正,如果我们想同步,那么微软已经为我们准备了一个叫做SyncRoot属性,我们不需要为了同步某个Collection集合额外定义一个锁对象,集合对象内部就已经替我们准备了一把叫做SyncRoot的锁。

同样的,其他的Collection类,也可以用这个方式实现线程安全。

发布了108 篇原创文章 · 获赞 126 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/qq_16587307/article/details/105345406