【问题标题】:Difference between Release and Debug?发布和调试之间的区别?
【发布时间】:2012-10-07 23:19:01
【问题描述】:

当我将 Visual Studio 2010 配置从“调试”更改为“发布”时,出现了一个非常奇怪的行为:

我有一个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

编辑

我们非常欢迎更好的方法!

【问题讨论】:

  • Eeeep,你可能应该考虑一种不同的方法来实现这个 while 循环可能会占用 CPU
  • 我在这里没有看到任何造成内存屏障的东西。调试限制了某些优化,在这种情况下不需要它,但发布版本需要它。
  • 我知道这不是问题所在,但您还在等什么?您可以在更改进度的方法中执行您等待的代码。或者您可以在 BackgroundWorkerCompleted 事件中执行此操作。
  • @BenjaminDangerJohnson 从查看他的代码来看,他似乎在等待用户指示它应该继续还是提前停止,所以将DoWork 的其余部分移动到ProgressChangedCompleted可能不是正确的解决方案。
  • @BenjaminDangerJohnson 尝试取消的问题是,您不知道从弹出窗口到注意到取消,BGW 将处理多少。如果做“额外”的工作是可以的,那很好,但如果不是,那么你仍然面临等待的问题。

标签: c# visual-studio-2010 while-loop backgroundworker release


【解决方案1】:

这可能是由于线程优化。为了在释放模式下安全地“看到”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,你也不必自己担心内存屏障。

【讨论】:

  • 非常感谢! ManualResetEvent 效果很好!我需要再设置一个问题'allowProgress = new ManualResetEvent(false);'每次我使用allowProgress.WaitOne(); 。我不能在allowProgress.Set(); 之后重复使用allowProgress.WaitOne(); 吗?
  • @Bolu 您可以在allowProgress 上调用Reset 以使其再次“阻止”:msdn.microsoft.com/en-us/library/… 或者,还有一个AutoResetEvent 类(取决于场景,它可能会更好)
  • 我已经尝试过了,这可以通过ResetAutoResetEvent 解决!非常感谢!
【解决方案2】:

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

但是,在这里标记字段 volatile 并不是一个好主意。您有一个更根本的问题,因为您正在执行旋转等待,这实际上总是一个坏主意。您应该使用一种实际暂停线程直到它应该继续的方法。一种方法是使用ManualResetEventSemaphore

查看您的代码,您正在等待用户关闭在进度更改事件中触发的消息框。我想说的是,您应该简单地将其包含在实际的“工作”事件中,而不是将其包含在进度更改事件中。 doWork 方法最好在触发后不关心进度更改事件。

【讨论】:

  • 我最终通过使用Application.Current.Dispatcher.Invoke将用户交互部分直接移动到DoWork事件以显示MessageBox。我这样做对吗?
  • @Bolu 好吧,如果你不利用 BGW 将事件编组到 UI 线程的能力,它有点违背 BGW 的目的,但你没有做的并不是真的错每说。这肯定比您在问题代码中所做的要好得多。
【解决方案3】:

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

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

在你想释放之后

semaphore.Release();

【讨论】:

  • 为什么信号量是更好的选择?
  • 它比 bool 方法更好。而且它非常易于使用,最后是保证。
猜你喜欢
  • 2016-12-16
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 2011-07-13
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
  • 1970-01-01
相关资源
最近更新 更多