文章目录
- 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 = 共享玩具箱(随便拿随便放)