发布和调试之间的区别



当我将Visual Studio 2010配置从Debug更改为Release时,我得到了一个非常奇怪的行为:

我有一个BackgroundWorker:_bg,在DoWork中我有:

iswaiting = true;
_bg.ReportProgress(1, filePath);
while (iswaiting)
{                        
;
}
//My other part of code (EDIT: something do to with the `result` I get from the user.)

ProgressChanged中,我有一个MessageBox,并且在用户交互之后,iswaiting将被设置回false,并且_bgDoWork程序将继续。

void _bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//my other part of code........
result = Microsoft.Windows.Controls.MessageBox.Show("Question" ,"Title", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
iswaiting=false; 
log(iswaiting.toString());                  
}

当我从Visual Studio运行它或在Debug模式下构建它时,所有这些都能很好地工作,但当我将它构建到Release时,我永远不会退出while(iswaiting)循环,尽管我可以从日志中看到iswaiting已经设置回false

编辑

更好的方法非常受欢迎

这可能是由于线程优化。为了在发布模式下安全地"看到"iswaiting的变化,您需要一个适当的内存屏障。

最简单的"修复"方法是将iswaiting标记为volatile:

volatile bool iswaiting;

话虽如此,像这样"旋转"将完全消耗一个CPU核心。一个更好的方法是使用ManualResetEvent来表示您可以继续。

// Add:
private ManualResetEvent allowProgress = new ManualResetEvent(false);

然后,不使用iswaiting,而是使用:

_bg.ReportProgress(1, filePath);
allowProgress.WaitOne(); // This will block until it's set

要允许这种情况继续,请使用:

result = Microsoft.Windows.Controls.MessageBox.Show("Question" ,"Title", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
allowProgress.Set();

这里的优点是,当你被阻塞时,你不会消耗CPU,而且你自己也不必担心内存障碍。

所以您的问题很可能是您使用了一个布尔字段,而您还没有将其标记为volatile。因此,某些优化(通常仅在发布模式下应用)可能导致两个线程都访问其线程本地的字段副本(例如,可能在其处理器核心的缓存上)。

然而,在这里标记字段volatile并不是一个好主意。你有一个更根本的问题,那就是你在执行spinwait,这实际上总是一个坏主意。您应该使用一个方法来暂停线程,直到它应该继续。一种方法是使用CCD_ 18或CCD_。

查看您的代码,您正在等待的是用户取消进度更改事件中触发的消息框。我想说的是,与其在进度更改事件中包含这一点,不如简单地将其包含在实际的"做工作"事件中。doWork方法最好在触发进度更改事件后不关心该事件。

更好的方法是使用可以继续的信号量信号。

private Semaphore semaphore = new Semaphore(1, 1000);
semaphore.WaitOne();

在你想发布之后

semaphore.Release();

最新更新