调试与发布C#代码
我有以下代码:调试与发布C#代码,c#,C#,我有以下代码: public static void Main(string[] args) { bool isComplete = false; var t = new Thread(() => { int i = 0; while (!isComplete) i += 0; }); t.Start(); Thread.Sleep
public static void Main(string[] args)
{
bool isComplete = false;
var t = new Thread(() =>
{
int i = 0;
while (!isComplete) i += 0;
});
t.Start();
Thread.Sleep(500);
isComplete = true;
t.Join();
Console.WriteLine("complete!");
}
程序将在发布模式下挂起,并在调试模式下给出输出(“完成!”)
原因是什么
谢谢。变量
isComplete
的值很可能被加载到发布版本中的寄存器中,以避免在while循环中从内存读取值的往返过程。因此,当
isComplete
的值更改为true时,循环将不会“检测”。您需要向编译器指出此变量是易失的:本质上是告诉系统不根据当前线程中执行的代码做出假设,无论此内存是否更改(即,其他线程或进程可能会更改它) 有关详细信息,请参阅此SA答案: -堆垛溢出 如果您想深入研究内存和并发性,那么下面是Fabian Giesen关于多核系统中缓存一致性的一篇优秀文章: 以下是一些很好的理由,说明除非你知道自己在做什么,否则你应该使用锁: -堆垛溢出 这是
Volatile上的MSDN文档。请阅读方法:
请注意,如果没有对这些术语进行更详细的解释(见上文SA thread或google),MSDN文档中的描述可能很难转化为实际情况
以下是volatile
关键字上的MSDN文档:
在C#中,这个关键字将使用半篱笆(据我所知);例如,您还可以在C中找到这个关键字,但它只影响编译器优化(不插入内存屏障),因为它最初用于读取内存映射的I/O
示例:
bool isComplete = false;
var t = new Thread(() =>
{
int i = 0;
while (!Volatile.Read(ref isComplete)) i += 0;
});
t.Start();
Thread.Sleep(500);
isComplete = true;
t.Join();
Console.WriteLine("complete!");
旁注:volatile
是编写正确的多线程代码的错误解决方案,但对于这个伪示例来说,它是好的。事实上,volatile
关键字并不是解决并发问题的神奇解决方案。实际问题需要仔细考虑所有可能的执行顺序,编译器或CPU可能的指令重新排序,在某些模糊的平台上,甚至核心之间的缓存合并。局部变量不能声明为可变的。这不会编译。他不应该使用关键字,使用半篱笆内存屏障:exVolatile.Read
。我认为这是一个更好的答案,如果它真的解释了Volatile
的功能(而不是“使程序工作”)。“为什么这个不安全的多线程代码行为不稳定”不是一个真正有用的问题。。。虽然这是一个有趣的编码难题,但它没有任何实际价值…@AlexeiLevenkov如果在生产环境的现实生活中看到这样的代码,人们会疑惑为什么它是错误的,不会像在开发机器上那样工作,然后决定在生产环境中运行调试构建,我不会感到惊讶。这一切都是因为他们没有意识到一些优化。