[英]Running a background task and waiting for its result without blocking the UI thread
我想在我的 WinForms 应用程序上创建一个启动画面。 我创建启动画面表单并运行初始化操作。
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
await Task.Run(InitializeAsync);
Application.Run(new frmMain());
}
//the InitializeAsync function
private async Task InitializeAsync()
{
splash.Status = "Test";
}
//the status property
public string Status
{
get { return status; }
set {
if (status != value)
{
status = value;
lblStatus.InvokeIfRequired(() => lblStatus.Text = value);
}
}
}
public static void InvokeIfRequired(this ISynchronizeInvoke snc,
MethodInvoker action)
{
if (snc.InvokeRequired) {
snc.Invoke(action, null);
} else {
action();
}
}
如您所见,我希望能够将 label 更改为当前状态。 但是,当我运行此代码时,只要我点击snc.Invoke(action, null);
行,程序挂起。 我做了一些研究/调试,似乎当我执行Task.Run
时,它会阻塞 UI 线程,并且由于在该线程上创建了启动画面,因此程序永远无法在 UI 线程上运行该操作。
我最终做的是等到任务完成并不断地做应用程序事件。
var task = Task.Run(InitializeAsync);
while(!task.IsCompleted)
{
Application.DoEvents();
Thread.Sleep(10);
}
Applicaiton.Run(new frmMain());
这行得通。 但是,我想知道是否有更优雅的解决方案。
首先,摆脱InvokeIfRequired
。 InvokeRequired
不管它有多常见——是一种严重的代码异味。
相反,您可以摆脱Task.Run
并直接调用InitializeAsync
:
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
await InitializeAsync();
Applicaiton.Run(new frmMain());
}
private async Task InitializeAsync()
{
splash.Status = "Test";
}
public string Status
{
get => lblStatus.Text;
set => lblStatus.Text = value;
}
如果您出于某种原因确实需要使用Task.Run
(即,如果InitializeAsync
有 CPU-bound 或阻塞工作要做),那么您可以使用Progress<T>
:
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
var progress = new Progress<string>(update => splash.Status = update);
await Task.Run(() => InitializeAsync(progress));
Applicaiton.Run(new frmMain());
}
private async Task InitializeAsync(IProgress<string>? progress)
{
progress?.Report("Test");
}
public string Status
{
get => lblStatus.Text;
set => lblStatus.Text = value;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.