直到最近,我一直假设在以下情况下设置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>
不是线程安全的,即使每个线程只是从集合的自己的部分获取/设置元素?
答案 0 :(得分:4)
请阅读:
https://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#Anchor_10
要回答您的问题,请不要 - 根据文档,它不保证是线程安全的。
即使当前的实现似乎是线程安全的(无论如何也不是这样),做出这个假设仍然是个坏主意。由于文档明确表示它不是线程安全的 - 未来版本可能合法地将底层实现更改为不再是线程安全的,并且打破您之前所依赖的任何假设。