ReadOnlyCollections和Threads - 这段代码安全吗?

时间:2012-11-29 18:30:22

标签: c# locking

我有一个带有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())
{
 ...
 ...
}

我认为我正在做的是线程安全的,不需要做任何锁定?我不确定的是,如果它在同一个集合被替换到另一个线程的同一时间发生,那么上面的查询会发生什么?

3 个答案:

答案 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);