Amz*_*ath 2 c# collections multithreading thread-safety
我有 System.Collections.Generic.SynchronizedCollection 共享集合。我们的代码使用 .Net 4.0 任务库来跨越线程并将同步集合传递给线程。到目前为止,线程还没有在集合中添加或删除项目。但是新的要求需要一个线程从集合中删除项目,而另一个线程只读取集合。在从集合中删除项目之前,我需要加锁吗?如果是这样,读取器线程是否是线程安全的?或建议获得线程安全的最佳方法?
不,它不是完全线程安全的。在一个简单的控制台应用程序中尝试以下操作,看看它是如何因异常而崩溃的:
var collection = new SynchronizedCollection<int>();
var n = 0;
Task.Run(
() =>
{
while (true)
{
collection.Add(n++);
Thread.Sleep(5);
}
});
Task.Run(
() =>
{
while (true)
{
Console.WriteLine("Elements in collection: " + collection.Count);
var x = 0;
if (collection.Count % 100 == 0)
{
foreach (var i in collection)
{
Console.WriteLine("They are: " + i);
x++;
if (x == 100)
{
break;
}
}
}
}
});
Console.ReadKey();
Run Code Online (Sandbox Code Playgroud)
请注意,如果您用 ConcurrentBag 替换 SynchronizedCollection,您将获得线程安全:
var collection = new ConcurrentBag<int>();
Run Code Online (Sandbox Code Playgroud)
SynchronizedCollection 在此应用程序中根本不是线程安全的。改用并发集合。
正如亚历山大已经指出的那样SynchronizedCollection,这种情况不是线程安全的。在SynchronizedCollection实际包装了一个正常的泛型列表,只是代表每次调用周围呼叫锁定基础列表。这也是在GetEnumerator. 所以枚举器的获取是同步的,但不是实际的枚举。
var collection = new SynchronizedCollection<string>();
collection.Add("Test1");
collection.Add("Test2");
collection.Add("Test3");
collection.Add("Test4");
var enumerator = collection.GetEnumerator();
enumerator.MoveNext();
collection.Add("Test5");
//The next call will throw a InvalidOperationException ("Collection was modified")
enumerator.MoveNext();
Run Code Online (Sandbox Code Playgroud)
使用 foreach 时,将以这种方式调用枚举器。因此,ToArray()在枚举此数组之前添加 a也不起作用,因为这将首先枚举到此数组中。当您在 foreach 内部执行操作时,此枚举可能会更快,因此可以降低出现并发问题的可能性。正如理查德指出的:为了真正的线程安全,去System.Collections.Concurrent类。
| 归档时间: |
|
| 查看次数: |
6629 次 |
| 最近记录: |