概述
System.Collections.Concurrent 命名空间提供了线程安全的集合类。在多线程环境中使用这些集合可以避免手动加锁,提升性能。
ConcurrentDictionary<TKey, TValue>
线程安全的字典实现。
基本操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| using System.Collections.Concurrent;
ConcurrentDictionary<string, int> dict = new ConcurrentDictionary<string, int>();
dict.TryAdd("apple", 1); dict.TryAdd("banana", 2);
if (dict.TryGetValue("apple", out int value)) { Console.WriteLine($"apple: {value}"); }
dict.AddOrUpdate("apple", addValueFactory: key => 1, updateValueFactory: (key, oldValue) => oldValue + 1);
dict.TryRemove("banana", out int removed);
int count = dict.GetOrAdd("orange", 0);
|
遍历
1 2 3 4 5 6 7 8 9 10 11
|
foreach (var kvp in dict) { Console.WriteLine($"{kvp.Key}: {kvp.Value}"); }
dict.ToList().ForEach(kvp => { dict.TryUpdate(kvp.Key, kvp.Value + 10, kvp.Value); });
|
ConcurrentQueue
FIFO 队列,无锁实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
queue.Enqueue(1); queue.Enqueue(2);
if (queue.TryDequeue(out int result)) { Console.WriteLine($"出队: {result}"); }
if (queue.TryPeek(out int peek)) { Console.WriteLine($"队首: {peek}"); }
async Task Producer(ConcurrentQueue<int> q, int items) { for (int i = 0; i < items; i++) { q.Enqueue(i); await Task.Delay(10); } }
async Task Consumer(ConcurrentQueue<int> q, CancellationToken ct) { while (!ct.IsCancellationRequested || q.Count > 0) { if (q.TryDequeue(out int item)) { Console.WriteLine($"处理: {item}"); } else { await Task.Delay(50); } } }
|
ConcurrentStack
LIFO 栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ConcurrentStack<int> stack = new ConcurrentStack<int>();
stack.Push(1); stack.PushRange(new[] { 2, 3, 4 });
if (stack.TryPop(out int result)) { Console.WriteLine($"弹出: {result}"); }
stack.TryPopRange(out int[] items, 3);
|
ConcurrentBag
无序集合,特别适合生产-消费模式中多个线程独立产生产品的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ConcurrentBag<string> bag = new ConcurrentBag<string>();
bag.Add("task1"); bag.Add("task2");
if (bag.TryTake(out string item)) { Console.WriteLine($"取出: {item}"); }
if (bag.TryPeek(out string peek)) { Console.WriteLine($"下一个: {peek}"); }
|
线程本地存储优化
ConcurrentBag 为每个线程维护一个本地队列,减少竞争。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ConcurrentBag<int> bag = new ConcurrentBag<int>();
Parallel.For(0, 1000, i => { bag.Add(i); });
Console.WriteLine($"总数: {bag.Count}");
int sum = 0; while (bag.TryTake(out int item)) { sum += item; }
|
BlockingCollection
支持阻塞操作的集合,通常包装 ConcurrentQueue。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| using System.Collections.Concurrent;
BlockingCollection<int> collection = new BlockingCollection<int>();
Task producer = Task.Run(() => { for (int i = 0; i < 10; i++) { collection.Add(i); Console.WriteLine($"生产: {i}"); Thread.Sleep(100); } collection.CompleteAdding(); });
Task consumer = Task.Run(() => { foreach (var item in collection.GetConsumingEnumerable()) { Console.WriteLine($"消费: {item}"); } });
await Task.WhenAll(producer, consumer);
|
有界集合
1 2 3 4 5 6 7
| BlockingCollection<int> bounded = new BlockingCollection<int>(10);
BlockingCollection<string> bagCollection = new BlockingCollection<string>( new ConcurrentBag<string>(), boundedCapacity: 20);
|
超时操作
1 2 3 4 5 6 7 8 9
| if (collection.TryAdd(42, TimeSpan.FromSeconds(1))) { Console.WriteLine("添加成功"); }
if (collection.TryTake(out int item, TimeSpan.FromSeconds(1))) { Console.WriteLine($"取出: {item}"); }
|
性能对比
| 集合 |
适用场景 |
性能特点 |
| ConcurrentDictionary |
键值查找频繁 |
读多写少时接近普通 Dictionary |
| ConcurrentQueue |
生产者-消费者队列 |
无锁,极高吞吐量 |
| ConcurrentStack |
LIFO 场景 |
无锁实现 |
| ConcurrentBag |
每个线程独立产生/消费 |
线程本地存储,适合并行循环 |
| BlockingCollection |
需要阻塞等待 |
包装器,可与任意 IProducerConsumerCollection 配合 |
完整示例:多阶段处理管道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| using System; using System.Collections.Concurrent; using System.Threading.Tasks;
class PipelineExample { static async Task Main() { var stage1 = new BlockingCollection<string>(10); var stage2 = new BlockingCollection<(string, int)>(10); var reader = Task.Run(() => { for (int i = 1; i <= 100; i++) { stage1.Add($"item_{i}"); } stage1.CompleteAdding(); }); var processor = Task.Run(() => { foreach (var item in stage1.GetConsumingEnumerable()) { int length = item.Length; stage2.Add((item, length)); } stage2.CompleteAdding(); }); foreach (var (item, length) in stage2.GetConsumingEnumerable()) { Console.WriteLine($"{item}: {length} chars"); } await Task.WhenAll(reader, processor); } }
|
注意事项
- Count 属性是 O(n):
ConcurrentBag.Count 和 ConcurrentQueue.Count 需要遍历
- 遍历快照:
GetEnumerator() 返回集合的快照
- 避免长时间持有锁:并发集合的原子操作短暂但并非零开销
- 与普通加锁集合的选择:高并发时使用 Concurrent 版本,低并发时普通集合加锁可能更快
1 2 3 4 5 6 7 8 9 10 11
| foreach (var item in concurrentBag) { }
while (concurrentBag.TryTake(out var item)) { Process(item); }
|
参考资源