【发布时间】:2021-05-22 13:01:50
【问题描述】:
我一直想把头绕在锁上,但我似乎无法弄清楚。 下面的代码使用了锁,但仍然给出“集合已修改”错误。我错过了什么?
class Program
{
static List<int> lEntries = new List<int>();
static readonly object entriesLock = new object();
public static List<int> Entries {
get { lock (entriesLock) { return lEntries; } }
set { lock (entriesLock) { lEntries = value; } }
}
static void Main(string[] args)
{
// Run 20 times to reproduce the issue more often
for (int i = 0; i < 20; i++)
{
Task.Run(() =>
{
for (int j = 0; j < 10000; j++)
{
Entries.Add(j);
}
});
Task.Run(() =>
{
for (int i = 0; i < 1000; i++)
{
Entries.Average(); // System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'
}
});
}
Console.ReadLine();
}
}
【问题讨论】:
-
我不知道你项目的用例。但我更推荐使用:ConcurrentBag
. -
这是个好建议。这个例子稍微简化了一点,因为我实际上使用的是 SortedList
。也许还有一个并发集合。 -
@M1sterPl0w 请不要推荐
ConcurrentBag<T>。这是一个extremely specialized 集合。它不是线程安全的List<T>。ConcurrentQueue<T>更可取,也不是线程安全的List<T>。 -
Jens 我已经向here 发布了一些关于如何使用锁的指南,您可能会觉得这些指南很有用。请注意,即使将
List<T>替换为ConcurrentQueue<T>之类的并发集合,在此集合上调用AverageLINQ 运算符仍将违反线程安全。只有这些集合的公共成员是线程安全的。通过这些集合实现的任何接口(包括扩展方法)访问的成员不能保证是线程安全的。 -
@TheodorZoulias 没有 ConcurrentBag 是线程保存...docs.microsoft.com/en-us/dotnet/api/…。我引用:“代表一个线程安全的、无序的对象集合。”。我不知道用例,我试图让 OP 找到他的解决方案,因此有更多的并发对象。当您搜索 ConcurrentBag 或 ConcurrentQueue 时,您会发现更多类似这样的数据类型...
标签: c# .net concurrency locking