2012-10-09 73 views
2

在我的应用程序中,我有一个图片框和2个按钮(“是”和“否”)。是将1添加到结果列表中,不添加0并且都转到下一张图片。 现在我需要在应用程序中实现一个计时器,如果没有提供答案,图片将转到下一个计时器。我想为它使用背景工作。Backgroundworker保持忙碌

下面的代码可以在我不点击按钮时切换图片。点击一个按钮会冻结UI,因为背景工作者保持“忙碌”状态。 我明白,CancelAsync不会立即停止backgroundworker,但DoWork中的返回语句实际上被打了。

所以我在这里的问题是为什么backgroundworker保持忙碌或者我完全在这里错误的方式?

protected override void OnLoad(EventArgs e) 
    { 
     base.OnLoad(e); 
     Counter = 0; 

     _backgroundWorker = new BackgroundWorker(); 
     _backgroundWorker.DoWork += _backgroundWorker_DoWork; 
     _backgroundWorker.WorkerSupportsCancellation = true; 
     _backgroundWorker.RunWorkerAsync(); 
    } 

    private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker bgw = sender as BackgroundWorker; 

     GoToNextItem(); //Show next picture 
     while (!bgw.CancellationPending) 
     { 
      _getNext = false; 
      Stopwatch sw = Stopwatch.StartNew(); 

      //Wait interval-time 
      while (!_getNext) 
      { 
       if ((sw.ElapsedMilliseconds > Test.Interval * 1000) && !bgw.CancellationPending) 
       { 
        _getNext = true; 
       } 
       if (bgw.CancellationPending) 
       { 
        e.Cancel = true; 
        return; //Breakpoint is hit here 
       } 
      } 
      if (_getNext) 
      { 
       Result.Add(0); 
       GoToNextItem(); 
      } 
     } 
     e.Cancel = true; 
    } 

    private void btnNo_Click(object sender, EventArgs e) 
    { 
     _backgroundWorker.CancelAsync(); 
     Result.Add(0); 

     while (_backgroundWorker.IsBusy) 
     { 
     _backgroundWorker.CancelAsync(); 
      System.Threading.Thread.Sleep(20); 
     } 
     _backgroundWorker.RunWorkerAsync(); 
    } 

    private void btnYes_Click(object sender, EventArgs e) 
    { 
     _backgroundWorker.CancelAsync(); 
     Result.Add(1); 

     while (_backgroundWorker.IsBusy) //Stays busy ==> UI freezes here 
     { 
      _backgroundWorker.CancelAsync(); 
      System.Threading.Thread.Sleep(20); 
     } 
     _backgroundWorker.RunWorkerAsync(); 
    } 

编辑

通过使用定时器由@Servy所建议的改变的代码。 欲了解关于背景问题的更多细节,请阅读已接受答案的评论。

回答

3

您应该只使用一个System.Windows.Forms.Timer为此。

private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); 
public Form1() 
{ 
    InitializeComponent(); 

    timer.Interval = 5000; 
    timer.Tick += timer_Tick; 
    timer.Start(); 
} 

private void timer_Tick(object sender, EventArgs e) 
{ 
    //runs in UI thread; code to go to next picture goes here 
} 

private void btnYes_Click(object sender, EventArgs e) 
{ 
    timer.Start(); 
} 
private void btnNo_Click(object sender, EventArgs e) 
{ 
    timer.Start(); 
} 

你要调用的是Start,没有按钮点击,以及因为它重置计时器,这样你会回到你的倒计时下一个画面的开始。

您可以随时拨打Stop停止发射。

至于为什么你当前的代码冻结了UI,这是因为你的点击事件处理程序在UI线程中运行,并且他们在等待后台工作人员时调用Sleep。我不会建议尝试修复这种方法,您应该使用计时器,但如果您确实想要,则需要将事件处理程序附加到后台工作人员的完成/取消事件,并执行您当前正在执行的所有操作在其他处理程序中“等到BGW准备就绪”,而不是在UI线程中等待。

+0

似乎可以更改我的代码。谢谢。这将解决我的问题,但是我仍然想知道为什么即使退出Do_Work,BGW仍然处于忙碌状态。我刚刚测试了另一种方法,通过执行_BackgroundWorker = new BackgroundWorker,添加事件并调用RunAsync。而这实际上工作。 – Koen

+1

@Koen正如我在我的回答中解释的,问题不在于'DoWork',问题在于你的按钮处理程序在等待GBW完成时正在休眠,这就是阻止UI的原因。 – Servy

+0

对不起,没有完全理解它,但在阅读@ JYelton的帖子后。我知道了。我当时的印象是退出DoWork完成BGW。 – Koen

1

当你点击一个按钮,你告诉后台工作者取消,但你也立即开始一个线程睡眠,这基本上不会给后台工作者足够的时间来有效取消。

我测试你的方法,并增加了Application.DoEvents()线如下:

while (_backgroundWorker.IsBusy) 
{ 
    _backgroundWorker.CancelAsync(); 
    Application.DoEvents(); 
    System.Threading.Thread.Sleep(20); 
} 

这使得后台工作,以更迅速地取消,但我相信这样的Application.DoEvents()使用是一个代码味道。我建议使用@Servy建议的方法。

+0

谢谢。我做到了。有人告诉我,如果可能的话,应该避免使用Application.DoEvents()。如果一个定时器对我来说更容易,我只是通过使用BGW开始了一个不好的电话。 – Koen

1

您使用的后台工作的方式是错误的我feel..you应该做哪些应该在后台,而不是使UI挂起运行的东西..

但在你的情况下,有没有这样的要求..

只需使用计时器控件来做同样的事情,并且还可以禁用GoToNextItem方法中的YES/NO按钮,并在末尾启用后退。