【发布时间】:2012-02-18 23:25:23
【问题描述】:
我在 32 位机器上运行,我可以使用以下代码 sn-p 来确认长值可能会撕裂。
static void TestTearingLong()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start();
System.Threading.Thread B = new System.Threading.Thread(ThreadB);
B.Start();
}
static ulong s_x;
static void ThreadA()
{
int i = 0;
while (true)
{
s_x = (i & 1) == 0 ? 0x0L : 0xaaaabbbbccccddddL;
i++;
}
}
static void ThreadB()
{
while (true)
{
ulong x = s_x;
Debug.Assert(x == 0x0L || x == 0xaaaabbbbccccddddL);
}
}
但是当我尝试使用双打类似的东西时,我无法得到任何撕裂。有谁知道为什么?据我从规范中可以看出,只有对浮点数的赋值是原子的。分配给替身应该有撕裂的风险。
static double s_x;
static void TestTearingDouble()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start();
System.Threading.Thread B = new System.Threading.Thread(ThreadB);
B.Start();
}
static void ThreadA()
{
long i = 0;
while (true)
{
s_x = ((i & 1) == 0) ? 0.0 : double.MaxValue;
i++;
if (i % 10000000 == 0)
{
Console.Out.WriteLine("i = " + i);
}
}
}
static void ThreadB()
{
while (true)
{
double x = s_x;
System.Diagnostics.Debug.Assert(x == 0.0 || x == double.MaxValue);
}
}
【问题讨论】:
-
愚蠢的问题 - 什么是撕裂?
-
在多线程访问方面保证对整数的操作是原子的。多头并非如此。撕裂是混合了两个中间值(坏)。他想知道为什么在双打中没有看到同样的情况,因为双打也不能保证原子操作。
-
@Oded:在 32 位机器上,一次只写入 32 位。如果您在 32 位机器上写入 64 位值,并在两个不同的线程上同时写入同一地址,您实际上有 四次 写入,而不是 两次,因为一次写入 32 位。因此线程有可能竞争,当烟雾消失时,变量包含一个线程写入的前 32 位和另一个线程写入的低 32 位。因此,您可以在一个线程上写入 0xDEADBEEF00000000,在另一个线程上写入 0x00000000BAADF00D,最终在内存中写入 0x0000000000000000。
-
@EricLippert - 所以,本质上是对 64 位值的操作在 32 位机器上不是原子的问题?
标签: c# .net multithreading atomic double-precision