C# 同步收集线程安全
我有System.Collections.Generic.SynchronizedCollection共享集合。我们的代码使用.NET4.0任务库跨线程并将同步的集合传递给线程。到目前为止,线程尚未向集合中添加或删除项。但是新的需求需要一个线程从集合中删除项,而另一个线程只读取集合。在从集合中删除项目之前是否需要添加锁?如果是这样,读线程是否是线程安全的?或者建议获得线程安全性的最佳方法?是的,SynchronizedCollection将为您执行锁定 如果您有多个读卡器而只有一个写卡器,您可能希望使用ReaderWriterLock,而不是SynchronizedCollectionC# 同步收集线程安全,c#,multithreading,collections,thread-safety,C#,Multithreading,Collections,Thread Safety,我有System.Collections.Generic.SynchronizedCollection共享集合。我们的代码使用.NET4.0任务库跨线程并将同步的集合传递给线程。到目前为止,线程尚未向集合中添加或删除项。但是新的需求需要一个线程从集合中删除项,而另一个线程只读取集合。在从集合中删除项目之前是否需要添加锁?如果是这样,读线程是否是线程安全的?或者建议获得线程安全性的最佳方法?是的,SynchronizedCollection将为您执行锁定 如果您有多个读卡器而只有一个写卡器,您可能
另外,如果您是.NET4+,请查看
System.Collections.Concurrent
。这些类的性能比SynchronizedCollection好得多。不,它不是完全线程安全的。在一个简单的控制台应用程序中尝试以下操作,看看它是如何在异常情况下崩溃的:
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();
var collection=newsynchronizedcollection();
var n=0;
任务。运行(
() =>
{
while(true)
{
集合。添加(n++);
睡眠(5);
}
});
任务。运行(
() =>
{
while(true)
{
Console.WriteLine(“集合中的元素:+collection.Count”);
var x=0;
if(collection.Count%100==0)
{
foreach(集合中的变量i)
{
Console.WriteLine(“它们是:”+i);
x++;
如果(x==100)
{
打破
}
}
}
}
});
Console.ReadKey();
请注意,如果使用ConcurrentBag替换SynchronizedCollection,您将获得线程安全性:
var collection = new ConcurrentBag<int>();
var collection=new ConcurrentBag();
SynchronizedCollection在此应用程序中不是线程安全的。请改用并发集合。正如Alexander已经指出的,
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();
var collection=newsynchronizedcollection();
集合。添加(“测试1”);
集合。添加(“测试2”);
集合。添加(“测试3”);
集合。添加(“测试4”);
var枚举器=collection.GetEnumerator();
枚举数。MoveNext();
集合。添加(“测试5”);
//下一个调用将抛出InvalidOperationException(“集合已修改”)
枚举数。MoveNext();
使用foreach时,将以这种方式调用枚举数。因此,在通过该数组枚举之前添加ToArray()
也不会起作用,因为这将首先枚举到此数组中。
当您在foreach中执行的操作时,此枚举可能会更快,因此它可以降低出现并发问题的概率。
正如Richard指出的:为了真正的线程安全,请使用
系统.Collections.Concurrent
类。如果您使用的是SynchronizedCollection,它应该已经为添加/从集合中删除的操作提供了适当的锁定。这是否也支持在从不同线程删除项时在另一个线程中枚举集合?我是否需要锁定枚举部分?现有“SynchronizedCollection”的全部要点是为所有操作提供线程安全的集合(它通过锁定所有操作来实现)。如果您使用的是ICollection中的内容,那么您可能需要自己锁定。请注意,如果您的目标是4.0 framework或更高版本,您可能会在使用System.Collections.Concurrent时看到性能的改进……同步收集对于多个读卡器和一个写入器是否是线程安全的?我认为这取决于使用收集的情况。例如:如果您试图通过特定值(例如属性值大于20的项)在集合中查找项,并更改该项,则您没有原子操作,但需要锁定IEnumerable扩展方法不安全(如Max()、Min()等)。如果在多线程环境中执行,则使用这些扩展方法会对您造成不利影响。但是,您可以在调用ToArray
之前使用集合的SyncRoot
属性。