极端线程安全集合
本文关键字:集合 安全 线程 | 更新日期: 2023-09-27 18:08:11
我在。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
的主要用法偏离了add/remove,并且您频繁枚举集合,那么您使用错误了。
如果你发布更多的代码,你会得到更多有针对性的帮助。并发性是理解并发性问题对于提供高性能解决方案非常重要的领域之一。
编辑: 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;
}
msdn声明:ConcurrentBag的所有public和protected成员都是线程安全的,可以在多个线程中并发使用。然而,通过ConcurrentBag实现的接口之一(包括扩展方法)访问的成员不能保证是线程安全的,并且可能需要调用者进行同步。