C#List <t>索引器线程安全

时间:2017-05-16 00:11:22

标签: c# thread-safety

直到最近,我一直假设在以下情况下设置List<T> via indexer的元素是线程安全的。

// Assumes destination.Count >= source.Count
static void Function<T,U>(List<T> source, Func<T,U> converter, List<U> destination)
{
    Parallel.ForEach(Partitioner.Create(0, source.Count), range =>
     {
         for(int i = range.Item1; i < range.Item2; i++)
         {
             destination[i] = converter(source[i]);
         }
     });
}

由于List<T>在内部将其元素存储在一个数组中,并且通过索引设置一个元素不需要调整大小,这似乎是一个合理的信念飞跃。但是,查看.NET Core中List<T>的{​​{1}},索引器的setter似乎会修改某个内部状态(见下文)。

// Sets or Gets the element at the given index.
public T this[int index]
{
    get
    {
        // Following trick can reduce the range check by one
        if ((uint)index >= (uint)_size)
        {
            ThrowHelper.ThrowArgumentOutOfRange_IndexException();
        }
        Contract.EndContractBlock();
        return _items[index];
    }

    set
    {
        if ((uint)index >= (uint)_size)
        {
            ThrowHelper.ThrowArgumentOutOfRange_IndexException();
        }
        Contract.EndContractBlock();
        _items[index] = value;
        _version++;
    }
}

所以我应该假设List<T>不是线程安全的,即使每个线程只是从集合的自己的部分获取/设置元素?

1 个答案:

答案 0 :(得分:4)

请阅读:

https://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#Anchor_10

要回答您的问题,请不要 - 根据文档,它不保证是线程安全的。

即使当前的实现似乎是线程安全的(无论如何也不是这样),做出这个假设仍然是个坏主意。由于文档明确表示它不是线程安全的 - 未来版本可能合法地将底层实现更改为不再是线程安全的,并且打破您之前所依赖的任何假设。