当我将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,_bg
DoWork
计划将继续
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
。
修改:
这样做的好方法非常受欢迎!!
答案 0 :(得分:6)
这可能是由于线程优化造成的。为了在发布模式下安全地“看到”iswaiting
中的更改,您需要一个内存屏障。
“修复”此问题的最简单方法是将iswaiting
标记为volatile
:
volatile bool iswaiting;
话虽如此,像这样“旋转”将完全消耗一个CPU核心。更好的方法是使用ManualResetEvent
表示您可以继续。
// Add:
private ManualResetEvent allowProgress = new ManualResetEvent(false);
然后,你不会使用等待,而是:
_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,而且你不必担心自己的内存障碍。
答案 1 :(得分:5)
因此,您的问题可能是您使用的是布尔字段,并且您还没有将其标记为volatile
。因此,某些优化(通常仅在发布模式下应用)可能导致两个线程访问其线程本地的字段副本(例如,可能在其处理器核心的高速缓存中)。
但是,在这里标记字段volatile
并不是一个好主意。你有一个更基本的问题,你正在执行一个spinwait,这实际上总是一个坏主意。您应该使用实际暂停线程的方法,直到它应该继续。一种方法是使用ManualResetEvent
或Semaphore
。
查看您的代码,您正在等待的是用户解除在进度更改事件中触发的消息框。我想说的是,你应该把它简单地包含在实际的“工作中”中,而不是在改进过程中将它包含在内。事件。一旦它被触发,doWork方法就不需要关心进度改变事件。
答案 2 :(得分:0)
更好的方法是使用可以继续的信号量信号。
private Semaphore semaphore = new Semaphore(1, 1000);
semaphore.WaitOne();
以及您要释放后
semaphore.Release();