所以我一直在阅读很多文章,这些文章一直在阻止使用Application.DoEvents(),甚至说它永远不会被使用,但我似乎无法为我的场景找到一个好的选择。 。 我正在处理的应用程序有一个方法,当主GUI表单首次启动时,由this.Shown事件调用该方法。该方法执行一些花费大约一分钟时间的工作,因此相同的方法也创建了一个基本上是自定义进度条的表单。请记住,此进程当前是单线程的,因此当此方法正在运行时,主GUI和进度条将变为无响应。如果用户在此期间点击任意位置,屏幕将变为空白。所以我正在努力将这个方法的一些工作放在BackgroundWorker线程中。以下是我的想法:
private BackgroundWorker Bgw = new BackgroundWorker();
private int LoadPercentage = 0;
//this sub is executed on the main UI thread
public void RunBgw()
{
bool StopThread = false;
//this object should be created in this method and needs to be updated as the child thread is doing work
MyCustomDialog dialog = new MyCustomDialog();
dialog.UpdateProgress(0, "My message");
dialog.Show();
this.Invalidate();
this.Refresh();
//critical properties to set if you want to report progress/be able to cancel the operation
Bgw.WorkerSupportsCancellation = true;
Bgw.WorkerReportsProgress = true;
//add handlers to Bgw so events will fire
Bgw.DoWork += new DoWorkEventHandler(Bgw_DoWork);
Bgw.ProgressChanged += new ProgressChangedEventHandler(Bgw_ProgressChanged);
Bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bgw_RunWorkerCompleted);
//fire off thread
Bgw.RunWorkerAsync();
while (Bgw.IsBusy == true)
{
if (BW.CancellationPending == true)
{
StopThread = true;
break;
}
Application.DoEvents();
if(LoadPercentage == 10)
{
dialog.UpdateProgress(LoadPercentage, "Still working...");
this.Invalidate();
this.Refresh();
}
if(LoadPercentage == 50)
{
dialog.UpdateProgress(LoadPercentage, "Halfway done...");
this.Invalidate();
this.Refresh();
}
// etc...
//slow down loop so it doesnt take up all the CPU
Thread.Sleep(200);
}
if(!StopThread) {
//continue with something else.
}
}
private void Bgw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker BgwLocal = sender as BackgroundWorker;
if ((BgwLocal.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
TimeConsumingWork();
BgwLocal.ReportProgress(10); //report progress back to the main UI thread
TimeConsumingWork();
BgwLocal.ReportProgress(15, SomeGuiIcon); //pass back specific gui icon
TimeConsumingWork();
BgwLocal.ReportProgress(50);
// etc...
}
}
private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
LoadPercentage = e.ProgressPercentage; //set current percentage of progress
if(LoadPercentage == 15)
{
GuiIcon1 = (Icon)e.UserState; //set gui icon
}
}
private void Bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
//error handling
}
else if (!(e.Error == null))
{
//error handling
}
else
{
//success
}
}
除了错误处理已被证明是困难和混乱之外,一切都运行良好。在更新主线程中的现有对象时,是否有更好的线程工作方式?
感谢阅读。
答案 0 :(得分:3)
您不应该使用以下代码阻止UI线程:
while (Bgw.IsBusy == true) { ... }
相反,允许RunBgw()
返回给调用者。使用BackgroundWorker上的事件来了解它何时完成。具体来说
Bgw.ProgressChanged += new ProgressChangedEventHandler(Bgw_ProgressChanged);
通过致电
报告进展情况Bgw_ProgressChanged
和
Bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bgw_RunWorkerCompleted);
原因
Bgw_RunWorkerCompleted
在完成BackgroundWorker时调用。
从Bgw_ProgressChanged
内更新进度条。
Windows UI是事件驱动的。您的代码没有使用事件来控制程序执行。