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
是否维持该保证(与使用可能会破坏此保证的专用存储块复制例程相反)。
答案 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循环是原子的。
最实用的是,您不必问这个问题。仅当您有另一个线程在没有任何同步的情况下访问数组时,原子性才重要。写入源阵列或从目标阵列读取。但是,这是有保证的线程竞赛。写到源数组最糟糕,副本具有旧数据和新数据的任意混合。从目标数组中读取数据会随机产生陈旧数据,就像线程错误通常那样。您必须非常勇于冒险使用此类代码。