我有一个带有ReadOnlyCollection的高度线程化的应用程序,如下所示:
internal static ReadOnlyCollection<DistributorBackpressure44> DistributorBackpressure44Cache
{
get
{
return _distributorBackpressure44;
}
set
{
_distributorBackpressure44 = value;
}
}
我在应用中有一个地方替换了这个集合(总是在一个单独的线程上),它看起来像这样:
CicApplication.DistributorBackpressure44Cache = new ReadOnlyCollection<DistributorBackpressure44>(someQueryResults.ToList());
我在代码中有很多地方可以访问这个集合,通常是通过Linq查询,在许多不同的线程中。代码通常看起来像这样:
foreach (DistributorBackpressure44 distributorBackpressure44 in CicApplication.DistributorBackpressure44Cache.Where(row => row.Coater == coater && row.CoaterTime >= targetTime).ToList())
{
...
...
}
我认为我正在做的是线程安全的,不需要做任何锁定?我不确定的是,如果它在同一个集合被替换到另一个线程的同一时间发生,那么上面的查询会发生什么?
答案 0 :(得分:5)
引用赋值是原子的,所以是的,它是线程安全的。但只要您不依赖于数据就可以在写入数据之后准备好进行读取。这是因为缓存,你可能想要使用volatile
来防止这种情况。
另见reference assignment is atomic so why is Interlocked.Exchange(ref Object, Object) needed?。
答案 1 :(得分:2)
这不太可能。在一个完全不可预测的时刻,在线程分配属性之后,其他线程将看到新的集合。在此之前,他们会读到陈旧的价值。哪个可能为null,或者可能是另一个内容完全不同的集合。
随机性会让你遇到麻烦。请注意,这与集合是否为ReadOnly无关。也许线程使用陈旧值是可以的,但这并不常见。最重要的是你没有提到它没关系,所以你可能还没有考虑过后果。你需要考虑一下。在属性getter和setter中你无法做任何事情,线程必须在它们之间进行协商。这就是使线程硬的原因,并且使您彻底分析代码中共享可变数据的位置非常重要。喜欢这个属性。
答案 2 :(得分:0)
听起来好像不会出现问题,但如果ReadOnlyCollection
包装的基础集合发生变化,则可能会遇到问题。
例如,以下代码块将抛出InvalidOperationException
,并显示消息“集合已被修改;枚举操作可能无法执行”,因为基础List<int>
有一个项目正在被删除列举在另一个线程上。
var numbers = new List<int>() {1,2,3,4,5};
var readOnly = new ReadOnlyCollection<int>(numbers);
ThreadPool.QueueUserWorkItem(x => {
foreach (int number in readOnly)
{
Console.WriteLine(number);
Thread.Sleep(300);
}
});
Thread.Sleep(150);
numbers.Remove(2);