我在一个单独的项目中有一些任务要异步执行。在接下来的几个月里,一切都如预期:
- 在开始时引发"已启动"事件,因此GUI可以启动繁忙指示器
- 在结束时(或发生异常时)会引发"Ended"-事件因此GUI可以停止繁忙指示符并处理内容
但一个新的要求是,我必须在编程后直接阅读版本,而无需任何用户交互。
我的第一个想法是从编程的"Ended"事件调用ReadVersion
。但是,执行此操作时,UI会冻结。繁忙指示器挂在"编程"状态,在读取版本之前无法移动窗口。通过按钮调用ReadVersion
时。单击不会冻结GUI。
项目逻辑:
protected void ProgramImage()
{
this.OnProgrammingStarted(new EventArgs());
this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(this.ProgramImageAsync)
.ContinueWith(
t =>
{
if (t.IsFaulted)
{
this.TaskExceptionHandlerProgramming(t);
}
else
{
this.ProgramImageAsyncDone();
}
},
this.taskScheduler);
}
public void ReadVersion()
{
this.OnVersionReadingStarted(new EventArgs());
this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task<string>.Factory.StartNew(ReadVersionAsync).ContinueWith(
t =>
{
if (t.IsFaulted)
{
this.TaskExceptionHandlerVersionReading(t);
}
else
{
this.ReadVersionAsyncDone(t.Result);
}
},
this.taskScheduler);
}
GUI:
private void OnProgrammingExecuted(object sender, WorkExecutedEventArgs e)
{
if (e.Error != null)
{
// Logging + messagebox
this.BiProgramming.IsBusy = false;
this.Restart();
return;
}
if (!e.Success)
{
// Logging + messagebox
this.BiProgramming.IsBusy = false;
this.Restart();
return;
}
this.BiProgramming.IsBusy = false; // Stops busyindicator
BL.ReadVersion(); // Window freezes and busyIndicator stays visible.
}
private void BtnReadVersionClick(object sender, RoutedEventArgs e)
{
BL.ReadVersion(); // works as expected
}
编辑
OnProgrammingExecuted
是当从ProgramImageAsyncDone
内引发事件时执行的方法。
方法ProgramImage
是从UI线程调用的。如果我没有错的话,这意味着TaskSheduler.FromCurrentSynchronizationContext
确保ProgramImageAsyncDone
(或TaskExceptionHandlerProgramming
)在UI线程上运行。
事实上,它以前没有引起任何问题,加强了我的看法。
问题在于我必须添加的行:对ReadVersion
的调用。添加此行之后,UI将冻结。但是从按钮调用此方法。单击不会冻结UI。
在搜索解决方案时,我遇到了Task.Run与Task.Factory.StartNew,我用Task.Run
重写了我的代码,看看这是否解决了我的问题。显然是这样。
更改代码:
protected void ProgramImage()
{
this.OnProgrammingStarted(new EventArgs());
this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() => this.ProgramImageAsync()).ContinueWith(
t =>
{
if (t.IsFaulted)
{
this.TaskExceptionHandlerProgramming(t);
}
else
{
this.ProgramImageAsyncDone();
}
},
this.taskScheduler);
}
public void ReadVersion()
{
this.OnVersionReadingStarted(new EventArgs());
Task.Run(() => ReadVersionAsync()).ContinueWith(
t =>
{
if (t.IsFaulted)
{
this.TaskExceptionHandlerVersionReading(t);
}
else
{
this.ReadVersionAsyncDone(t.Result);
}
},
this.taskScheduler);
}