发布和调试之间的区别?

Bol*_*olu 6 c# release backgroundworker visual-studio-2010 while-loop

当我将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.)
Run Code Online (Sandbox Code Playgroud)

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());                  
    }
Run Code Online (Sandbox Code Playgroud)

所有这些作品非常好,当我从Visual Studio运行或建造中调试模式,但是当我建立它松开,我从来没有走出的while(iswaiting)循环,但我可以看到该日志iswaiting已被设置回false.

编辑:

更好的方式是非常欢迎!

Ree*_*sey 6

这可能是由于线程优化造成的.为了安全地"看到" iswaiting发布模式中的更改,您需要一个内存屏障.

"修复"这个的最简单方法是标记iswaitingvolatile:

volatile bool iswaiting;
Run Code Online (Sandbox Code Playgroud)

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

// Add:
private ManualResetEvent allowProgress = new ManualResetEvent(false);
Run Code Online (Sandbox Code Playgroud)

然后,你不会使用等待,而是:

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

要允许此操作继续,请使用:

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

  allowProgress.Set();
Run Code Online (Sandbox Code Playgroud)

这里的优点是,在您被阻止时不会消耗CPU,并且您不必担心自己的内存障碍.


Ser*_*rvy 5

所以你的问题可能是你使用的是布尔字段,而你没有将其标记为volatile.正因为如此,某些优化(通常只在释放模式中应用)可以导致两个线程访问是本地它们的线程(可能在他们的处理器的核的高速缓存,例如)的场的拷贝.

但是,在volatile这里标记这个领域并不是一个好主意.你有一个更基本的问题,你正在执行一个spinwait,这实际上总是一个坏主意.您应该使用实际暂停线程的方法,直到它应该继续.一种方法是使用a ManualResetEvent或a Semaphore.

查看您的代码,您正在等待的是用户解除在progress progress事件中触发的消息框.我想说的是,你应该将它包含在实际的"do work"事件中,而不是在进度改变事件中使用它.doWork方法一旦被触发就不需要关心进度改变事件.