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参数的值,用于指定排序的依据。

385

2023.09.04

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

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

1016

2023.10.19

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

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

62

2025.10.17

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

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

389

2025.12.29

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

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

480

2023.08.10

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

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

143

2025.12.24

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

11

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

21

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

18

2026.01.13

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
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号