在日常的開發中,經常會遇到多個線程對同一個集合進行讀寫操作,就難免會出現線程安全問題。
以下代碼,如果使用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;