1、线程安全集合
BlockingCollection:一个支持界限和阻塞的容器(线程安全集合),与队列结构相似,常用函数如下
Add :向容器中插入元素
TryTake:从容器中取出元素并删除
TryPeek:从容器中取出元素,但不删除。
CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。
IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。
普通用法示例:
/// <summary> /// 线程安全集合用法 /// </summary> public static void BC() { //线程安全集合 using (BlockingCollection<int> blocking = new BlockingCollection<int>()) { int NUMITEMS = 10000; for (int i = 1; i < NUMITEMS; i++) { blocking.Add(i); } //完成添加 blocking.CompleteAdding(); int outerSum = 0; // 定义一个委托方法取出集合元素 Action action = () => { int localItem; int localSum = 0; //取出并删除元素,先进先出 while (blocking.TryTake(out localItem)) { localSum += localItem; } //两数相加替换第一个值 Interlocked.Add(ref outerSum, localSum); }; //并行3个线程执行,多个线程同时取集合的数据 Parallel.Invoke(action, action, action); Console.WriteLine($"0+...{NUMITEMS-1} = {((NUMITEMS * (NUMITEMS - 1)) / 2)},输出结果:{outerSum}"); //此集合是否已标记为已完成添加且为空 Console.WriteLine($"线程安全集合.IsCompleted={blocking.IsCompleted}"); } }
限制集合长度示例
/// <summary> /// 限制集合长度 /// </summary> public static void BCLimtLength() { //限制集合长度为5个,后面进的会阻塞等集合少于5个再进来 BlockingCollection<int> blocking = new BlockingCollection<int>(5); var task1= Task.Run(() => { for (int i = 0; i < 20; i++) { blocking.Add(i); Console.WriteLine($"集合添加:({i})"); } blocking.CompleteAdding(); Console.WriteLine("完成添加"); }); // 延迟500ms执行等待先生产数据 var task2 = Task.Delay(500).ContinueWith((t) => { while (!blocking.IsCompleted) { var n = 0; if (blocking.TryTake(out n)) { Console.WriteLine($"取出:({n})"); } } Console.WriteLine("IsCompleted = true"); }); Task.WaitAll(task1, task2); }
在BlockingCollection中使用Stack(栈,先进后出)示例:
/// <summary> /// 线程安全集合,先进后出 /// </summary> public static void BCStack() { //线程安全集合,参数表明栈标识,队列长度为5 BlockingCollection<int> blocking = new BlockingCollection<int>(new ConcurrentStack<int>(), 5); var task1 = Task.Run(() => { for (int i = 0; i < 20; i++) { blocking.Add(i); Console.WriteLine($"集合添加:({i})"); } blocking.CompleteAdding(); Console.WriteLine("完成添加"); }); // 等待先生产数据 var task2 = Task.Delay(500).ContinueWith((t) => { while (!blocking.IsCompleted) { var n = 0; if (blocking.TryTake(out n)) { Console.WriteLine($"取出:({n})"); } } Console.WriteLine("IsCompleted = true"); }); Task.WaitAll(task1, task2); }
2、线程安全字典
ConcurrentDictionary :这个比较好理解,普通字典多线程并发时添加时会报错,而这个则是线程安全的,不会报错。
普通字典示例:
//普通字典 private static IDictionary<string, string> Dictionaries { get; set; } = new Dictionary<string, string>(); /// <summary> /// 字典增加值 /// </summary> public static void AddDictionaries() { Stopwatch sw = new Stopwatch(); sw.Start(); //并发1000个线程写 Parallel.For(0, 1000, (i) => { var key = $"key-{i}"; var value = $"value-{i}"; // 不加锁会报错 // lock (Dictionaries) // { Dictionaries.Add(key, value); // } }); sw.Stop(); Console.WriteLine("Dictionaries 当前数据量为: {0}", Dictionaries.Count); Console.WriteLine("Dictionaries 执行时间为: {0} ms", sw.ElapsedMilliseconds); }
不加锁时结果:
加锁后:
线程安全字典示例:
//线程安全字典 private static IDictionary<string, string> ConcurrentDictionaries { get; set; } = new ConcurrentDictionary<string, string>(); / <summary> /// 线程安全字典添加值 /// </summary> public static void AddConcurrentDictionaries() { Stopwatch sw = new Stopwatch(); sw.Start(); //并发1000个线程写 Parallel.For(0, 1000, (i) => { var key = $"key-{i}"; var value = $"value-{i}"; // 无须加锁 ConcurrentDictionaries.Add(key, value); }); sw.Stop(); Console.WriteLine("ConcurrentDictionaries 当前数据量为: {0}", ConcurrentDictionaries.Count); Console.WriteLine("ConcurrentDictionaries 执行时间为: {0} ms", sw.ElapsedMilliseconds); }
可以看到,线程安全字典比普通字典性能略好,且线程安全字典无需加锁。
1