我使用Caliburn Micro的协同程序来创建一个工作流,该工作流还显示WPF中的屏幕。
我为繁忙屏幕制作了这个IDisposable结构:将using语句包装在长时间运行的作业周围,在该作业期间将显示繁忙屏幕。当using块结束时,Dispose()会将繁忙的屏幕隐藏在幕后。
using scope构造使用..显示和隐藏繁忙屏幕。。
Application.Current.Dispatcher.Invoke(...show/hide form...)
在UI线程上实现:
使用示例:
yield return new TaskResult(Task.Factory.StartNew(() =>
{
using (_dialogController.ShowDialogForUsingScope(new BusyViewModel()
{ Message = "Account gegevens ophalen..." }))
{
_securityContext.RefreshAccountFromServer();
}
using (_dialogController.ShowDialogForUsingScope(new BusyViewModel()
{ Message = "You see me..." }))
{
_securityContext.RefreshAccountFromServer();
}
}));
这非常有效:繁忙的屏幕显示,工作完成,它被删除,在第二项工作期间立即出现另一个繁忙的屏幕并完成:)
但当我用2个收益率返回时,第二个作业无论如何都会在UI线程上运行:
Debug.WriteLine("Thread: " + Thread.CurrentThread.ManagedThreadId + ", IsBackground: " + Thread.CurrentThread.IsBackground);
yield return new TaskResult(Task.Factory.StartNew(() =>
{
Debug.WriteLine("Thread: " + Thread.CurrentThread.ManagedThreadId + ", IsBackground: " + Thread.CurrentThread.IsBackground);
using (_dialogController.ShowDialogForUsingScope(new BusyViewModel() { Message = "Account gegevens ophalen..." }, 2000))
{
_securityContext.RefreshAccountFromServer();
}
}));
Debug.WriteLine("Thread: " + Thread.CurrentThread.ManagedThreadId + ", IsBackground: " + Thread.CurrentThread.IsBackground);
yield return new TaskResult(Task.Factory.StartNew(() =>
{
Debug.WriteLine("Thread: " + Thread.CurrentThread.ManagedThreadId + ", IsBackground: " + Thread.CurrentThread.IsBackground);
using (_dialogController.ShowDialogForUsingScope(new BusyViewModel() { Message = "You never see me..." }, 2000))
{
}
}));
导致:
Thread: 9, IsBackground: False
Thread: 12, IsBackground: True
Thread: 9, IsBackground: False
Thread: 9, IsBackground: False --> 9 too ??
这解释了为什么它会冻结,但为什么任务不在另一个线程上?
在谷歌上搜索了很多之后,我找到了这个彻底的答案:
Task.Factory.StartNew()是否保证使用调用线程以外的其他线程?
在某些情况下,任务调度程序似乎倾向于在请求线程中运行任务(在我的情况下是在UI线程中同步运行)。
为了防止这种情况,您需要在启动任务时传递默认调度程序,而不是当前调度程序(这是默认行为):
示例:
yield return new TaskResult(Task.Factory.StartNew(() =>
{
using (_dialogController.ShowDialogForUsingScope(new BusyViewModel() { Message = "Inloggen..." }, 800))
{
// do stuff
}
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default));