c# – 极端线程安全集合
作者:互联网
我在.Net 4.5中有一个ConcurrentBag,我从数据库中存储了大约4,000行.我正在存储DTO.
我的整个应用依赖于此.我有返回整个列表的函数,还有返回单个项目的函数.我的代码中有很多地方我正在对集合进行LINQ查询等.
我将它全部投入生产,在现场获得可观的流量,并立即100%cpu.我使用了iis诊断工具,果然,死锁中有50个线程,等待ConcurrentBag.
文档说这个集合是线程安全的,但要么不是这样,要么这个集合的性能不好,从而间接地使它不是线程安全的.
遗憾的是,这个集合不是只读的.如果通过ID查找的其中一个函数返回null,它将访问Web服务并添加它.
我也将它转换为ConcurrentDictionary,并遇到了同样的问题.在.Values属性上锁定天数.
在大多数极端情况下,最快且最安全的线程解决方案是什么?
private ConcurrentBag<Students> _students;
public static ConcurrentBag<DestinyHash> GetStudents()
{
if (_students == null) { _students = new ConcurrentBag<Students>(); }
return _students;
}
public static Student GetStudentByID(int id)
{
if (GetStudents().Any(x => x.id == id)) { return ... }
_students.Add(getStudentFromDb(id));
return...
}
示例用法 – 遍布整个应用程序.
Helper.GetStudents().FirstOrDefault(x => x.name == "foo" && x.status == "bar");
Helper.GetStudentByID(50);
解决方法:
简单的答案是你使用了错误的容器.
ConcurrentBag不是通用的.它可以更像是可重用对象池,您可以(通常作为最后一步)将其减少为单个非并发值.可以使用的一个问题是同时汇总列表.
如果您对ConcurrentBag的主要用法来自添加/删除,并且您经常枚举该集合,那么您使用它是错误的.
如果您发布更多代码,您将获得更多有针对性的帮助.并发性是理解问题对于提供高性能解决方案非常重要的领域之一.
编辑:
ConcurrentDictionary将适用于您正在做的事情.诀窍是你不想使用ConcurrentDictionary.Values – 这将锁定字典并复制其内容.如果你只是使用它的IEnumerable< T>界面,你会没事的.例如:
private ConcurrentDictionary<int,Student> _students;
public static IEnumerable<Student> GetStudents()
{
return _students.Select(x => x.Value);
}
public static Student GetStudentByID(int id)
{
Student s;
if(_students.TryGetValue(id, out s)) return s;
s = getStudentFromDb(id);
_students[id] = s;
return s;
}
标签:c,multithreading,linq,concurrency,concurrentdictionary 来源: https://codeday.me/bug/20190528/1169427.html