gpt4 book ai didi

c# - 此代码在 Release模式下挂起,但在 Debug模式下工作正常

转载 作者:IT王子 更新时间:2023-10-29 03:34:48 26 4
gpt4 key购买 nike

我遇到了这个问题,想知道在调试和 Release模式下出现这种行为的原因。

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 变量上缺少“volatile”关键字所愚弄。

当然,你不能添加它,因为它是一个局部变量。当然,因为它是一个局部变量,所以根本不需要它,因为局部变量保存在堆栈中,它们自然总是“新鲜的”。

但是,经过编译后,它不再是局部变量。由于它是在匿名委托(delegate)中访问的,因此代码被拆分,并被翻译成一个帮助类和成员字段,类似于:

public static void Main(string[] args)
{
TheHelper hlp = new TheHelper();

var t = new Thread(hlp.Body);

t.Start();

Thread.Sleep(500);
hlp.isComplete = true;
t.Join();
Console.WriteLine("complete!");
}

private class TheHelper
{
public bool isComplete = false;

public void Body()
{
int i = 0;

while (!isComplete) i += 0;
}
}

我现在可以想象,多线程环境中的 JIT 编译器/优化器,在处理 TheHelper 类时,实际上可以缓存falseBody() 方法开始的某个寄存器或堆栈帧中,并且在方法结束之前永远不会刷新它。那是因为没有保证线程和方法不会在“=true”被执行之前结束,所以如果没有保证,那么为什么不缓存它并获得一次读取堆对象而不是每次读取它的性能提升迭代。

这正是关键字 volatile 存在的原因。

为了让这个辅助类在多线程环境中 正确 稍微好一点 1),它应该有:

    public volatile bool isComplete = false;

但是,当然,因为它是自动生成的代码,所以您不能添加它。更好的方法是在对 isCompleted 的读写操作周围添加一些 lock(),或者改用其他一些现成的同步或线程/任务实用程序尝试在裸机上实现它(它不会裸机,因为它是 CLR 上的 C#,具有 GC、JIT 和 (..))。

Debug模式的差异可能是因为在 Debug模式下许多优化被排除在外,所以您可以调试您在屏幕上看到的代码。因此 while (!isComplete) 未优化,因此您可以在那里设置断点,因此 isComplete 不会在方法开始时主动缓存在寄存器或堆栈中并被读取在每次循环迭代时从堆上的对象中获取。

顺便说一句。那只是我的猜测。我什至没有尝试编译它。

顺便说一句。这似乎不是错误;它更像是一个非常模糊的副作用。另外,如果我是对的,那么它可能是语言缺陷——C# 应该允许将“volatile”关键字放在局部变量上,这些局部变量被捕获并提升为闭包中的成员字段。

1) 请参阅下文,了解 Eric Lippert 关于 volatile 的评论和/或这篇非常有趣的文章,其中显示了确保代码依赖于 所涉及的复杂程度>volatile 安全的 ..呃, ..呃,让我们说好吧。

关于c# - 此代码在 Release模式下挂起,但在 Debug模式下工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41632387/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com