Array.Copy是否在每个元素的基础上维护有关原子读写的保证?

时间:2018-07-14 20:40:02

标签: c# .net atomic

C#确保certain types always have atomic reads and writes。在两个类型的数组上调用Array.Copy时,我有同样的保证吗?每个元素都是原子读写的吗?我浏览了the source code的一些内容,但没有得到一个可靠的答案。

例如,如果我滚动自己的代码来复制两个数组...

static void Copy<T>(T[] source, T[] destination, int length)
{
    for (int i = 0; i < length; ++i)
        destination[i] = source[i];
}

...并称为Copy<int>变体,这保证了每个元素都从source原子读取,并原子写入destination,因为C#承诺int读取并写是原子的。我只是问Array.Copy是否维持该保证(与使用可能会破坏此保证的专用存储块复制例程相反)。

1 个答案:

答案 0 :(得分:9)

Array.Copy()尝试通过使用memmove() CRT函数(不考虑数组中存储的实际类型)的原始内存到内存副本来提高复制效率。如果数组元素的类型小于自然处理器的字长,则可以大大提高效率。

因此,您需要知道memmove()是否可以提供原子性保证。这是CLR程序员明确回答的一个棘手问题。原子性是对象引用的基本特征,当垃圾收集器无法自动更新这些引用时,它们将无法正常运行。因此,程序员在CLR代码中对此进行了特殊处理,comment he provided会告诉您您想知道的内容(经过编辑以适合您的情况):

// The CRT version of memmove does not always guarantee that updates of 
// aligned fields stay atomic (e.g. it is using "rep movsb" in some cases).
// Type safety guarantees and background GC scanning requires object 
// references in GC heap to be updated atomically.

那是一种非常悲观的人生观。但是显然不,当CLR作者没有做出此假设时,您不能假设Array.Copy()是原子的。


实际考虑可能确实需要占上风。在合理通用的体系结构上,x86和x64具有memmove()实现,不会使CLR内存模型保证更糟,它们一次复制4或8个对齐的字节。而且实际上,由于不能保证T,所以不能保证通用代码替换中的for循环是原子的。

最实用的是,您不必问这个问题。仅当您有另一个线程在没有任何同步的情况下访问数组时,原子性才重要。写入源阵列或从目标阵列读取。但是,这是有保证的线程竞赛。写到源数组最糟糕,副本具有旧数据和新数据的任意混合。从目标数组中读取数据会随机产生陈旧数据,就像线程错误通常那样。您必须非常勇于冒险使用此类代码。