【发布时间】:2011-04-10 09:39:34
【问题描述】:
阅读this question,我想测试是否可以证明在此类操作的原子性无法保证的类型上读写的非原子性。
private static double _d;
[STAThread]
static void Main()
{
new Thread(KeepMutating).Start();
KeepReading();
}
private static void KeepReading()
{
while (true)
{
double dCopy = _d;
// In release: if (...) throw ...
Debug.Assert(dCopy == 0D || dCopy == double.MaxValue); // Never fails
}
}
private static void KeepMutating()
{
Random rand = new Random();
while (true)
{
_d = rand.Next(2) == 0 ? 0D : double.MaxValue;
}
}
令我惊讶的是,即使执行了整整三分钟,断言也不会失败。 是什么赋予了?
- 测试不正确。
- 测试的特定时序特征使得断言不太可能/不可能失败。
- 概率太低了,我必须运行更长时间的测试才能触发它。
- CLR 提供比 C# 规范更强大的原子性保证。
- 我的操作系统/硬件提供比 CLR 更强的保证。
- 还有别的吗?
当然,我不打算依赖规范未明确保证的任何行为,但我希望对该问题有更深入的了解。
仅供参考,我在两个不同环境中的 Debug 和 Release(将 Debug.Assert 更改为 if(..) throw)配置文件上运行了这个:
- Windows 7 64 位 + .NET 3.5 SP1
- Windows XP 32 位 + .NET 2.0
编辑:为了排除 John Kugelman 的评论“调试器不是薛定谔安全”成为问题的可能性,我将行 someList.Add(dCopy); 添加到 KeepReading 方法并验证此列表没有看到一个陈旧的缓存中的值。
编辑:
根据 Dan Bryant 的建议:使用 long 而不是 double 几乎会立即破坏它。
【问题讨论】:
-
我的猜测(也就是这样)是#3。
-
我会检查 IL 以确保编译器没有玩任何技巧,但除此之外我什么都没有。我希望这会在 32 位机器上中断。
-
我会增加写入线程的数量 - 尝试让 N+1 线程在 N 核系统上运行。
-
尝试使用 Int64 而不是 double 运行它。 FPU 是 Intel CPU 上的一个独立的野兽,它有自己的寄存器堆栈,能够存储 8 字节浮点数。请务必使用跨越两个单词的数字。
-
该线程中那个可悲地被忽视的答案中提到了它。可怜的家伙。它主要取决于 JIT 编译器在加载程序堆中分配变量的确切位置。这是你无法控制的。不确定加载器堆的对齐规则是什么,尝试在静态双精度之前和之后添加更多不同大小的静态。您可以在调试器的内存窗口中获取它们的地址(如 &d)。
标签: c# .net thread-safety double atomic