三大并发集合ConcurrentDictionary、ConcurrentBag、ConcurrentQueue

文章目录

  • 1、ConcurrentQueue(并发队列)
    • [1.1 🎯 本质原理](#1.1 🎯 本质原理)
    • [1.2 ⚙️ 工作原理](#1.2 ⚙️ 工作原理)
    • [1.3 ✅ 优点](#1.3 ✅ 优点)
    • [1.4 ❌ 缺点](#1.4 ❌ 缺点)
    • [1.5 🎪 使用场景](#1.5 🎪 使用场景)
  • 2、ConcurrentDictionary(并发字典)
    • [2.1 🎯 本质原理](#2.1 🎯 本质原理)
    • [2.2 ⚙️ 工作原理](#2.2 ⚙️ 工作原理)
    • [2.3 ✅ 优点](#2.3 ✅ 优点)
    • [2.4 ❌ 缺点](#2.4 ❌ 缺点)
    • [2.5 🎪 使用场景](#2.5 🎪 使用场景)
  • 3、ConcurrentBag(并发包)
    • [3.1 🎯 本质原理](#3.1 🎯 本质原理)
    • [3.2 ⚙️ 工作原理](#3.2 ⚙️ 工作原理)
    • [3.3 ✅ 优点](#3.3 ✅ 优点)
    • [3.4 ❌ 缺点](#3.4 ❌ 缺点)
    • [3.5 🎪 使用场景](#3.5 🎪 使用场景)
  • [4、📊 三大并发集合对比](#4、📊 三大并发集合对比)
  • [5、🎯 选择指南 - 一句话总结](#5、🎯 选择指南 - 一句话总结)
  • [6、🔧 实际代码示例](#6、🔧 实际代码示例)
    • [6.1 场景1:Web服务器请求处理(推荐Queue)](#6.1 场景1:Web服务器请求处理(推荐Queue))
    • [6.2 场景2:全局用户会话缓存(推荐Dictionary)](#6.2 场景2:全局用户会话缓存(推荐Dictionary))
    • [6.3 场景3:数据库连接池(推荐Bag)](#6.3 场景3:数据库连接池(推荐Bag))

1、ConcurrentQueue(并发队列)

1.1 🎯 本质原理

就像银行排队取号机 - 先来先服务

csharp 复制代码
// 内部实现:无锁链表
头 → 元素1 → 元素2 → 元素3 → 尾

1.2 ⚙️ 工作原理

  • 入队:新元素加到尾部

  • 出队:从头部取出元素

  • 多线程安全:使用原子操作,避免锁竞争

1.3 ✅ 优点

csharp 复制代码
// 生产者-消费者场景性能极高
var queue = new ConcurrentQueue<string>();

// 生产者线程
Task.Run(() => queue.Enqueue("任务1"));  // 快速入队

// 消费者线程  
Task.Run(() => {
    if (queue.TryDequeue(out var task))  // 快速出队
    {
        // 处理任务
    }
});

1.4 ❌ 缺点

  • 只能按顺序访问(先进先出)

  • 不能随机访问中间元素

  • 内存开销稍大(链表节点)

1.5 🎪 使用场景

csharp 复制代码
// 1. 消息队列
ConcurrentQueue<Message> messageQueue;

// 2. 任务调度
ConcurrentQueue<WorkItem> taskQueue;

// 3. 日志记录
ConcurrentQueue<LogEntry> logQueue;

2、ConcurrentDictionary(并发字典)

2.1 🎯 本质原理

就像酒店的房卡管理系统 - 通过钥匙快速找到房间

csharp 复制代码
// 内部:分段锁哈希表
分区1 { "Key1": "Value1", "Key2": "Value2" } ← 锁1
分区2 { "Key3": "Value3", "Key4": "Value4" } ← 锁2
// 不同分区的操作互不干扰

2.2 ⚙️ 工作原理

  • 分段锁:字典分成多个区域,每个区域有自己的锁

  • 读写分离:多个线程可以同时读不同区域

  • 智能锁定:只锁定正在修改的小区域

2.3 ✅ 优点

csharp 复制代码
var cache = new ConcurrentDictionary<string, User>();

// 线程安全的读写操作
cache["user1"] = new User();  // 自动处理锁
cache.TryRemove("user2", out _);  // 安全的删除

// 原子操作 - 非常强大!
cache.AddOrUpdate("counter", 1, (key, old) => old + 1);

2.4 ❌ 缺点

  • 内存占用比普通字典大

  • 某些批量操作仍需额外同步

  • 迭代时可能看到不一致的数据

2.5 🎪 使用场景

csharp 复制代码
// 1. 共享缓存
ConcurrentDictionary<string, CacheItem> globalCache;

// 2. 计数器统计
ConcurrentDictionary<string, int> requestCounters;

// 3. 会话存储
ConcurrentDictionary<string, UserSession> userSessions;

3、ConcurrentBag(并发包)

3.1 🎯 本质原理

就像家里的杂物抽屉 - 东西随便放,随便拿,没有顺序

csharp 复制代码
// 内部:线程本地存储 + 偷取机制
线程1的袋子: [物品A, 物品B]   ← 线程1优先从这里取
线程2的袋子: [物品C, 物品D]   ← 线程2优先从这里取
// 如果自己的袋子空了,就去"偷"别人的

3.2 ⚙️ 工作原理

  • 线程亲和性:每个线程有自己的存储区域

  • 工作窃取:线程可以偷其他线程的数据

  • 无序访问:没有固定顺序,性能优化为目的

3.3 ✅ 优点

csharp 复制代码
var bag = new ConcurrentBag<WorkItem>();

// 同一个线程频繁操作性能极佳
Task.Run(() => {
    bag.Add(workItem1);  // 加到自己的袋子
    bag.Add(workItem2);
    
    // 优先从自己的袋子取,很快!
    if (bag.TryTake(out var item)) {
        Process(item);
    }
});

3.4 ❌ 缺点

  • 完全无序 - 不能控制取出顺序

  • 不适合需要顺序的场景

  • 线程间数据迁移有开销

3.5 🎪 使用场景

csharp 复制代码
// 1. 对象池(最佳场景!)
ConcurrentBag<DbConnection> connectionPool;

// 2. 临时结果收集
ConcurrentBag<Result> partialResults;

// 3. 工作项缓存
ConcurrentBag<WorkItem> workItemCache;

4、📊 三大并发集合对比

特性 ConcurrentQueue ConcurrentDictionary ConcurrentBag
数据结构 队列(FIFO) 键值对 无序集合
顺序保证 ✅ 严格先进先出 ✅ 按键访问 ❌ 完全无序
性能特点 生产消费快 按键查找快 同线程操作快
线程安全 无锁算法 分段锁 工作窃取
内存开销 中等 较高 较低

5、🎯 选择指南 - 一句话总结

选择 ConcurrentQueue 当:
"我需要处理任务队列,先来的任务先处理"

csharp 复制代码
// 消息处理、任务调度、请求队列
ConcurrentQueue<Request> requestQueue;

选择 ConcurrentDictionary 当:
"我需要快速通过钥匙找到对应的东西"

csharp 复制代码
// 缓存、配置、会话存储、计数器
ConcurrentDictionary<string, CacheItem> cache;

选择 ConcurrentBag 当:
"我有一堆东西,谁需要谁拿,顺序不重要"

csharp 复制代码
// 对象池、资源池、临时存储
ConcurrentBag<Resource> resourcePool;

6、🔧 实际代码示例

6.1 场景1:Web服务器请求处理(推荐Queue)

csharp 复制代码
public class RequestProcessor
{
    private ConcurrentQueue<HttpRequest> _requestQueue = new ConcurrentQueue<HttpRequest>();
    
    // 多个请求同时入队
    public void EnqueueRequest(HttpRequest request)
    {
        _requestQueue.Enqueue(request);
    }
    
    // 工作线程按顺序处理
    public void ProcessRequests()
    {
        while (_requestQueue.TryDequeue(out var request))
        {
            HandleRequest(request);  // 保证先来先处理
        }
    }
}

6.2 场景2:全局用户会话缓存(推荐Dictionary)

csharp 复制代码
public class SessionManager
{
    private ConcurrentDictionary<string, UserSession> _sessions = new ConcurrentDictionary<string, UserSession>();
    
    // 快速通过SessionId找到用户会话
    public UserSession GetSession(string sessionId)
    {
        if (_sessions.TryGetValue(sessionId, out var session))
            return session;
        return null;
    }
    
    // 安全的添加或更新
    public void UpdateSession(string sessionId, UserSession session)
    {
        _sessions[sessionId] = session;  // 自动线程安全
    }
}

6.3 场景3:数据库连接池(推荐Bag)

csharp 复制代码
public class ConnectionPool
{
    private ConcurrentBag<DbConnection> _connections = new ConcurrentBag<DbConnection>();
    
    // 获取连接 - 哪个线程需要就从池里拿
    public DbConnection GetConnection()
    {
        if (_connections.TryTake(out var connection))
            return connection;  // 复用现有连接
        
        return CreateNewConnection();  // 创建新连接
    }
    
    // 归还连接 - 放回池里供其他人使用
    public void ReturnConnection(DbConnection connection)
    {
        _connections.Add(connection);  // 无序存放,性能好
    }
}

💡 记住这个比喻:

  • ConcurrentQueue = 银行排队(先来先服务)

  • ConcurrentDictionary = 酒店房卡(按键找房)

  • ConcurrentBag = 共享玩具箱(随便拿随便放)

相关推荐
火锅机器4 小时前
java 8 lambda表达式对list进行分组
java·开发语言·list
shaominjin1234 小时前
android在sd卡中可以mkdir, 但是不可以createNewFile
android·开发语言·python
筱砚.4 小时前
【STL——stack容器】
开发语言·c++
曦樂~4 小时前
【Qt】定时器--滚动相册
开发语言·qt
沐知全栈开发5 小时前
Java 文档注释
开发语言
时光追逐者5 小时前
一个使用 WPF 开发的 Diagram 画板工具(包含流程图FlowChart,思维导图MindEditor)
c#·.net·wpf·流程图
程序_白白5 小时前
探讨一下java将来未来两年内的就业以及发展
java·开发语言
哼?~5 小时前
C++之智能指针
开发语言·c++
mjhcsp5 小时前
C++ long long 类型深度解析:大整数处理的基石
开发语言·c++·策略模式·long long