ToArray()可以抛出异常吗?

Joe*_*ger 9 c# collections multithreading locking

虽然这个问题的答案非常好,但它意味着您应该锁定对List.ToArray()的锁定以进行并发. 这篇博文还暗示它可能会灾难性地失败(但很少).在枚举列表或其他集合时,我通常使用ToArray而不是锁定,以避免"Collection Modified,Enumeration may not completed"异常.这个答案和博客文章都对这个假设提出质疑.

List.ToArray()的文档没有列出任何异常,所以我一直认为它总是会完成(虽然可能是陈旧的数据),虽然从数据一致性的角度来看它不是线程安全的,但它是线程从代码执行的角度来看是安全的 - 换句话说,它不会抛出异常并且调用它不会破坏底层集合的内部数据结构.

如果这个假设不正确,那么虽然它从未引起过问题,但它可能是高可用性应用程序中的定时炸弹.什么是明确的答案?

Nik*_*vić 6

ToArray由于一个简单的原因,您将找不到有关方法可能例外的文档.这是一种具有许多"重载"的扩展方法.它们都具有相同的方法签名,但实现对于不同的集合类型是不同的,例如List<T>HashSet<T>.

但是,对于大多数代码而言,我们可以安全地假设.NET框架BCL由于性能原因而不执行任何锁定.我也非常具体地检查了ToListfor的实现List<T>.

public T[] ToArray()
{
    T[] array = new T[this._size];
    Array.Copy(this._items, 0, array, 0, this._size);
    return array;
}
Run Code Online (Sandbox Code Playgroud)

正如您可能想象的那样,它是非常简单的代码,最终会执行mscorlib.对于此特定实现,您还可以查看在Array.Copy方法的MSDN页面中可能出现的异常.如果列表的排名在刚刚分配目标数组后立即更改,则归结为异常.

考虑到这List<T>是一个微不足道的例子,你可以想象异常的机会在需要更复杂的代码以便存储在数组中的结构上升.实施Queue<T>是一个更有可能失败的候选人:

public T[] ToArray()
{
    T[] array = new T[this._size];
    if (this._size == 0)
    {
        return array;
    }
    if (this._head < this._tail)
    {
        Array.Copy(this._array, this._head, array, 0, this._size);
    }
    else
    {
        Array.Copy(this._array, this._head, array, 0, this._array.Length - this._head);
        Array.Copy(this._array, 0, array, this._array.Length - this._head, this._tail);
    }
    return array;
}
Run Code Online (Sandbox Code Playgroud)


usr*_*usr 5

当文档或原则没有明确保证线程安全时,您不能假设它。如果您确实假设它,您就有将一类错误投入生产的风险,这些错误是不可调试的,并且有可能使您损失大量的生产力/可用性/金钱。你愿意承担这个风险吗?

你永远不能测试某些东西是线程安全的。你永远无法确定。您无法确定未来版本的行为方式是否相同。

以正确的方式进行操作并锁定。

顺便说一句,这些评论是针对List.ToArray哪个是更安全的ToArray. 我理解为什么人们会错误地认为它可以与写入列表同时使用。当然IEnumerable.ToArray 不可能是线程安全的,因为这是底层序列的属性。