0

0

c# SortedList 和 SortedDictionary 的线程安全问题

煙雲

煙雲

发布时间:2026-01-12 08:03:26

|

619人浏览过

|

来源于php中文网

原创

SortedList 和 SortedDictionary 默认非线程安全,多线程读写会引发异常或数据不一致;加锁需覆盖所有访问路径且防死锁;推荐用 ConcurrentDictionary 配合手动排序实现安全有序操作。

c# sortedlist 和 sorteddictionary 的线程安全问题

SortedList 和 SortedDictionary 默认都不是线程安全的

直接在多线程环境下读写同一个 SortedListSortedDictionary 实例,会触发 InvalidOperationException(如“集合已修改;枚举操作可能无法执行”),或产生数据不一致、索引越界等未定义行为。.NET 官方文档明确标注二者均 不保证线程安全,即使只读访问多个线程同时遍历,也可能因底层结构被其他线程修改而失败。

为什么不能靠加锁“简单包一层”就高枕无忧

加锁能避免崩溃,但容易忽略两个关键点:

  • 所有访问路径(包括 ContainsKeyTryGetValueKeysValuesGetEnumerator())都必须使用同一把锁,漏掉任意一个就可能出问题
  • KeysValues 属性返回的是动态视图,不是快照——如果在遍历 myDict.Keys 时另一个线程修改了字典,迭代器立刻失效
  • 嵌套调用(比如在锁内调用一个外部方法,而该方法又间接访问了同一个字典)可能引发死锁或锁粒度失控

常见错误写法:

lock (_lock) {
    if (dict.ContainsKey(key)) { // ✅ 加锁
        dict[key] = value;       // ✅ 加锁
    }
}
// 但下面这行没锁,且返回的 ICollection 不是线程安全的:
var keys = dict.Keys.ToList(); // ❌ 可能中途被改,ToList() 过程中抛异常

真正安全的替代方案:ConcurrentDictionary + 手动排序逻辑

ConcurrentDictionary 是唯一内置线程安全的键值集合,但它不维持顺序。若业务强依赖有序遍历(如按 key 升序取前 N 项),只能放弃自动排序,改用以下组合:

千博购物系统.Net
千博购物系统.Net

千博购物系统.Net能够适合不同类型商品,为您提供了一个完整的在线开店解决方案。千博购物系统.Net除了拥有一般网上商店系统所具有的所有功能,还拥有着其它网店系统没有的许多超强功能。千博购物系统.Net适合中小企业和个人快速构建个性化的网上商店。强劲、安全、稳定、易用、免费是它的主要特性。系统由C#及Access/MS SQL开发,是B/S(浏览器/服务器)结构Asp.Net程序。多种独创的技术使

下载
  • ConcurrentDictionary 存储数据,保障并发读写安全
  • 需要有序结果时,临时提取 KeysToArray(),再用 OrderBy()Array.Sort() 排序(注意:这是快照,不影响原集合)
  • 若频繁按范围查询(如 “key ∈ [a,b)”),可额外维护一个 ConcurrentBag + 定期重建排序数组,或引入第三方库如 System.Collections.Concurrent.ConcurrentSortedList(非 .NET 内置,需 NuGet)

示例(安全取最小 key 对应的 value):

var keys = concurrentDict.Keys.ToArray();
if (keys.Length > 0) {
    var minKey = keys.Min(); // 或 Array.Sort(keys); var minKey = keys[0];
    concurrentDict.TryGetValue(minKey, out var value);
}

SortedDictionary 在高并发写入下的性能陷阱

虽然 SortedDictionary 基于红黑树,插入/查找平均 O(log n),但它的内部节点操作不是原子的。多线程写入时,即使加了锁,也会因锁竞争导致吞吐量急剧下降——尤其当写操作占比超过 20%,性能可能比单线程还差。相比之下,ConcurrentDictionary 的分段锁机制在写密集场景下更稳定。

容易被忽略的一点:SortedList 在大量插入后内存碎片更严重(底层是数组),扩容时需复制整个数组,此时锁持有时间变长,进一步加剧争用。除非你确定是读多写少 + 数据量小(IList 接口,否则别为“看起来更省内存”选它。

相关专题

更多
sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

384

2023.09.04

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1011

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

60

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

367

2025.12.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

479

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

143

2025.12.24

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

78

2026.01.09

c++框架学习教程汇总
c++框架学习教程汇总

本专题整合了c++框架学习教程汇总,阅读专题下面的文章了解更多详细内容。

45

2026.01.09

学python好用的网站推荐
学python好用的网站推荐

本专题整合了python学习教程汇总,阅读专题下面的文章了解更多详细内容。

118

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号