C# 使用ConcurrentBag类处理集合线程安全问题

在日常的开发中,经常会遇到多个线程对同一个集合进行读写操作,就难免会出现线程安全问题。

以下代码,如果使用List<T>就会遇到问题:System.InvalidOperationException:“集合已修改;可能无法执行枚举操作。”。原因是timer2在遍历list的过程当中,timer1修改了list,使其大小发生了变化。所以我们应该使用线程安全的集合来处理。不管是读还是写,同一时刻只能做一件事情,要么读,要么写。

    class Program
    {
        private static List<string> list = new List<string>();

        static void Main(string[] args)
        {
            var count = 0;

            //任务一
            Timer timer1 = new Timer((obj) =>
            {
                var str = "a" + ++count;
                list.Add(str);
                Console.WriteLine("添加了:" +str);
            }, null, 0, 1000);

            //任务二
            Timer timer2 = new Timer((obj) =>
            {
                foreach (var item in list)
                {
                    Console.WriteLine("显示:" + item);
                }
            }, null, 0, 1000);

            Console.ReadLine();
        }
    }

改成:ConcurrentBag<T>就不会了,因为ConcurrentBag<T>是线程安全的。

   class Program
    {
        private static ConcurrentBag<string> list = new ConcurrentBag<string>();

        static void Main(string[] args)
        {
            var count = 0;

            //任务一
            Timer timer1 = new Timer((obj) =>
            {
                var str = "a" + ++count;
                list.Add(str);
                Console.WriteLine("添加了:" +str);
            }, null, 0, 1000);

            //任务二
            Timer timer2 = new Timer((obj) =>
            {
                foreach (var item in list)
                {
                    Console.WriteLine("显示:" + item);
                }
            }, null, 0, 1000);

            //任务三
            Timer timer3 = new Timer((obj) =>
            {
                foreach (var item in list)
                {
                    Console.WriteLine("删除了:" +item);
                    list.TryTake(out string result);
                }
            }, null, 0, 3000);

            Console.ReadLine();
        }
    }

参考网址:https://blog.csdn.net/boonya/article/details/80541460

 

另外:

如果想在删除时也是线程安全的,也可使用BlockingCollection<T>类。

先把需要删除的找出来,然后再遍历删除

                foreach (var id in ids)
                {
                    var hub = _hubs.FirstOrDefault(m => m.Id == id);
                    if (hub != null)
                    {
                        _hubs.TryTake(out hub);
                    }
                }
private BlockingCollection<HubModel> _hubs;

 

posted @ 2019-11-12 14:44  屌丝大叔的笔记  阅读(3201)  评论(0编辑  收藏  举报