Async-await 旨在阻止您的程序在相对较短的时间内空闲等待另一个进程完成。考虑等待数据库查询的结果、读取文件、从数据库中获取数据等。
如果您使用 async-await,您的线程不会在其他处理正在执行请求时空闲等待。相反,如果可以环顾四周,看看是否还有其他事情要做。
当然,您可以通过创建一个单独的线程来等待您来实现相同的效果。除此之外,这会使您的代码更加复杂,创建单独的线程相当耗时。
如果你想启动一个长时间运行的进程,想想几秒钟,也许几分钟甚至更长,启动这个单独线程所需的时间不再重要了。
所以在你的情况下,我建议使用BackgroundWorker
如果您的后台工作人员需要大量程序来完成其工作,请考虑从 BackgroundWorker 派生并开始在 BackgroundWorker.OnDoWork 的覆盖下工作。
在您的情况下,backgroundworker 只需要一个小函数,因此订阅 DoWork 事件就足够了。
使用你的 Visual Studio 工具箱添加一个后台工作者,或者在你的构造函数中手动添加它:
// Constructor:
public Form1()
{
InitializeComponent();
// Create a backgroundworker
this.backgroundWorker = new BackgroundWorker
{
// only if you want to display something during processing:
WorkerReportsProgress = true,
WorkerSupportsCancellation = true; // to neatly close your form
};
this.backgroundWorker.DoWork += this.BackgroundProcedure;
this.backgroundWorker.ProgressChanged += this.BackgroundProcessReport;
this.backgroundWorker.RunworkerCompleted += this.BackgroundWorkerFinished;
}
启动和停止很容易:
bool IsBackGroundWorkerBusy => this.backgroundWorker.IsBusy;
void StartBackgroundWork()
{
if (this.IsBackGroundWorkerbusy) return; // already started;
this.DisplayBackgroundWorkerActive(); // for example, show an ajax loader
this.backgroundWorker.RunworkerAsync();
// or if you want to start with parameters:
MyParameters backgroundWorkerParameters = new MyParameters(...);
this.backgroundWorker.RunworkerAsync(backgroundWorkerParameters);
}
void RequestCancelBackgroundWork()
{
this.DisplayBackgroundWorkerStopping();
this.backgroundWorker.CancelAsync();
}
后台程序。它由后台线程执行。
您不能在此过程中调用任何与 UI 相关的函数。
如果您想更新 UI 中的任何内容,请使用 ReportProgress。
void BackGroundProcedure(object sender, DoworkEventArgs e)
{
// if you know that the backgroundworker is started with parameters:
MyParameters parameters = (MyParameters)e.Argument;
// do you work, regularly check if cancellation is requested:
while (!e.Cancel)
{
...
// only if progress reports are needed: report some progress, not too often!
MyProgressParams progressParams = new MyProgressParams(...);
this.ReportProgress(..., progressParams);
}
// if here, the thread is requested to cancel
// if needed report some result:
e.Result = ...;
}
进度报告。它由您的 UI 线程执行,因此如果需要,您可以更新 UI 元素。这是此方法的主要功能。
报告进度中的第一个参数是一个数字,通常从 0..100 开始,进度事件的接收者使用它来更新有关进度的一些视觉显示。如果您没有任何迹象表明进度需要多长时间,请不要使用该值。 progressParams 可以是任何对象。
void BackgroundProcessReport(object sender, ProgressChangedEventArgs e)
{
// the background worker reported some progress.
... // update UI
}
Runworker Completed 在线程完成时调用。它包含分配给 e.Result 的数据。它由 UI 线程执行,所以你可以做任何你想做的与 UI 相关的事情:
void BackgroundWorkerFinished(object sender, RunWorkerCompletedEventArgs e)
{
this.DisplayBackgroundWorkerFinished(); // for example: hide ajax loader
... // use e to process result
}
表格的整齐闭合
如果您的表单正在关闭,则必须在处理窗口之前完成后台工作程序。正确的方法是使用 OnClosing 事件:
bool closeFormRequested = false;
void OnFormClosing(object sender, CancelEventArgs e)
{
// if background worker busy: request cancellation; can't close the form right now
if (this.IsBackgroundworkerBusy)
{
this.closeFormRequested = true;
e.Cancel = true;
this.RequestCancelBackgroundWork();
}
else
{ // background worker not busy, OK to close
e.Cancel = false;
}
}
一段时间后,后台工作人员报告它已完成:
void BackgroundWorkerFinished(object sender, RunWorkerCompletedEventArgs e)
{
this.DisplayBackgroundWorkerFinished();
... // process result
// if requested: close the form:
if (this.closeFormRequested)
this.Close();
}
this.Close()会导致OnFormClosing,不过这次后台worker不会忙,会继续关闭
最后:后台工作者实现了 IDisposable,当你的表单被 Disposed 时不要忘记 Dispose。