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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM