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;