在我的应用程序中,我有一个图片框和2个按钮(“是”和“否”)。是在结果列表中添加1,否则添加0并且两者都转到下一张图片。 现在我需要在应用程序中实现一个计时器,如果没有提供答案,则将图片转到下一个。我想用它来做背景工作。
当我没有点击按钮时,下面的代码可以很好地切换图片。单击按钮会冻结UI,因为后台工作人员保持“忙碌”状态。 我确实理解CancelAsync不会立即停止后台工作,但DoWork中的return语句实际上已被命中。
所以我的问题是为什么背景工作者保持忙碌或者我在这里完全走错路?
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建议的定时器更改代码。 有关backgroundworker-question的更多详细说明,请阅读已接受答案的评论。
答案 0 :(得分: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线程中等待。
答案 1 :(得分:1)
当你点击一个按钮时,你告诉后台工作人员取消,但你也立即开始一个线程睡眠,这基本上不会给后台工作者足够的时间来有效地取消。
我测试了您的方法并添加了Application.DoEvents()
行,如下所示:
while (_backgroundWorker.IsBusy)
{
_backgroundWorker.CancelAsync();
Application.DoEvents();
System.Threading.Thread.Sleep(20);
}
这允许后台工作者更快地取消,但我相信以这种方式使用Application.DoEvents()
是代码气味。我建议使用@Servy建议的方法。
答案 2 :(得分:1)
你使用后台工作者的方式是错误的我觉得..你应该做的事情应该在后台运行,而不是让UI挂起..
但是在你的情况下没有这样的要求..
只需使用计时器控制来执行相同操作,并在GoToNextItem方法中禁用“是/否”按钮并在结束时启用...