C# 常用并发集合

在 C# 中,并发集合提供了线程安全的数据结构,允许多个线程同时访问而无需显式的锁定。这些集合位于 System.Collections.Concurrent 命名空间中,并广泛用于多线程和并发编程中。以下是一些常用的并发集合及其使用场景和示例:

1. ConcurrentDictionary<TKey, TValue>

功能
  • 线程安全的键值对集合,允许多个线程同时读取和写入数据。
  • 内置了锁的分段机制,以便多个线程可以高效并发操作。
使用场景
  • 当多个线程需要共享和更新键值对时使用。
  • 常用于缓存、共享状态的并发访问。
示例
using System.Collections.Concurrent;

ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>();

// 添加或更新键值对
dict.TryAdd(1, "value1");
dict[2] = "value2";

// 尝试获取值
if (dict.TryGetValue(1, out string value))
{
    Console.WriteLine($"Key 1: {value}");
}

// 添加或更新现有的值
dict.AddOrUpdate(1, "newValue", (key, oldValue) => oldValue + " updated");

2. ConcurrentQueue<T>

功能
  • 线程安全的队列,遵循先进先出(FIFO)原则。
  • 无需锁定的并发访问,可以安全地从多个线程同时进行入队和出队操作。
使用场景
  • 适用于生产者-消费者模式,其中多个线程添加任务,多个线程处理任务。
  • 用于任务调度、事件处理等场景。
示例
using System.Collections.Concurrent;

ConcurrentQueue<int> queue = new ConcurrentQueue<int>();

// 入队操作
queue.Enqueue(1);
queue.Enqueue(2);

// 出队操作
if (queue.TryDequeue(out int result))
{
    Console.WriteLine($"Dequeued: {result}");
}

3. ConcurrentStack<T>

功能
  • 线程安全的栈,遵循后进先出(LIFO)原则。
  • 支持并发的 Push 和 Pop 操作,无需显式锁定。
使用场景
  • 适用于后进先出的任务处理模式,例如需要最近任务优先处理。
示例
using System.Collections.Concurrent;

ConcurrentStack<int> stack = new ConcurrentStack<int>();

// 入栈操作
stack.Push(1);
stack.Push(2);

// 出栈操作
if (stack.TryPop(out int result))
{
    Console.WriteLine($"Popped: {result}");
}

4. ConcurrentBag<T>

功能
  • 线程安全的无序集合,适合频繁的添加和移除操作。
  • 每个线程都有自己的本地存储,减少了竞争。
使用场景
  • 适合需要快速添加或删除项目但不关心顺序的情况,如需要保存大量对象用于重用。
示例
using System.Collections.Concurrent;

ConcurrentBag<int> bag = new ConcurrentBag<int>();

// 添加元素
bag.Add(1);
bag.Add(2);

// 移除元素
if (bag.TryTake(out int result))
{
    Console.WriteLine($"Took: {result}");
}

5. BlockingCollection<T>

功能
  • 提供了线程安全的集合,支持阻塞和限速的操作。
  • 常与 ConcurrentQueue<T>ConcurrentStack<T> 搭配使用。
  • 支持生产者-消费者模式,可以在集合为空时自动阻塞消费者线程,或者在达到容量时阻塞生产者线程。
使用场景
  • 适用于生产者-消费者场景,其中生产者生产数据,消费者等待并消费数据。
  • 支持最大容量限制,控制数据的生产和消费速率。
示例
using System.Collections.Concurrent;

BlockingCollection<int> collection = new BlockingCollection<int>(boundedCapacity: 5);

// 生产者任务
Task.Run(() =>
{
    for (int i = 0; i < 10; i++)
    {
        collection.Add(i);
        Console.WriteLine($"Produced: {i}");
    }
    collection.CompleteAdding();
});

// 消费者任务
Task.Run(() =>
{
    foreach (var item in collection.GetConsumingEnumerable())
    {
        Console.WriteLine($"Consumed: {item}");
    }
});

6. ConcurrentQueue<T> vs BlockingCollection<T>

  • ConcurrentQueue<T>:非阻塞,适合无等待的场景,多个线程可以并发读取和写入。
  • BlockingCollection<T>:阻塞,当队列为空时,消费者等待;当队列达到上限时,生产者等待,适用于有生产消费节奏控制的场景。

7. ConcurrentDictionary<TKey, TValue> vs Dictionary<TKey, TValue>

  • ConcurrentDictionary<TKey, TValue>:线程安全,允许并发的读写操作,内部采用了分段锁以提高并发性能。
  • Dictionary<TKey, TValue>:非线程安全,需要通过显式的锁来保证安全性。

总结

并发集合提供了一种简化并发编程的方式,避免了手动使用锁来保护共享数据。在多线程程序中,选择合适的并发集合可以显著提高性能并减少代码的复杂性。常用并发集合包括:

  • ConcurrentDictionary<TKey, TValue>:适合多线程安全的键值对存储。
  • ConcurrentQueue<T>ConcurrentStack<T>:适合先进先出和后进先出的任务调度。
  • ConcurrentBag<T>:适合无序的快速添加和删除。
  • BlockingCollection<T>:适合生产者-消费者模式。

了解这些并发集合的特点和使用场景,将帮助你在多线程编程中编写更高效和安全的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生命不息-学无止境

你的每一份支持都是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值