我的WPF窗口有一个奇怪的行为。总而言之,我有一个WPF窗口,可以在加载的事件
上执行异步操作if (AppContext.OnlineMode)
Task.Run(() => SynchronizeMails());
这个函数(synchronizeMails),做了很多东西(异步联系webservice,插入数据库,刷新GUI,......),并且在第一次启动时,需要很长时间。
我有一个按钮,允许用户断开连接,绑定到显示soBox的命令,具体取决于当前状态。对于我的情况,synchronizeMails将bool设置为true,以防止多次同步并防止在治疗期间退出。我的命令实现看看这个布尔值,并显示一个messageBox,如果当前正在同步。
ExitCommand = new RelayCommand(p => Task.Run(() =>
{
RestartAsked = true;
if (Synchronizing)
{
OpeningView.ShowWarning("...");
}
});
由于样式原因,我们在自己的实现中重新编码了消息框,因此对messageBox ShowWarning的调用只是主GUI上的ShowDialog,没有异步/等待的东西。
奇怪的是来到这里:当用户在这个消息框上单击OK时,我的异步方法就停止了它的工作,执行最终的bloc(如果有一些错误,整个方法在try-finally块中禁用我的布尔值)当然,这项工作还没有完成。
我不明白为什么showdialog with return使我的异步方法停止...
有没有人有想法?
答案 0 :(得分:3)
我发现这是因为我的异步内容在Parallel.For和Parallel.ForEach中,这使得它在结束前返回
并行性和异步性是两种不同的形式的并发性,它们并不能很好地协同工作。
当您拥有受多个核心受益的CPU限制算法时,应该使用并行性。当您有I / O绑定或事件触发的代码时,应该使用异步。
异步联系webservice,插入数据库,......
听起来像异步是适当的并发形式,而不是并行性。
要正确应用异步,请从"离开" (即,执行webservice调用的代码和在数据库中插入的代码),并将该代码更改为使用异步API(使用await
)。由于您使用的是await
,因此您必须将该方法更改为async
,并将其返回类型更改为Task
/ Task<T>
。然后,该方法的所有调用者都需要使用await
,并成为async
等。
最终,您最终会得到一个真正异步的正确SynchronizeMailsAsync
方法,并且可以从您的加载事件处理程序中调用:
if (AppContext.OnlineMode)
await SynchronizeMailsAsync();
请注意,Task.Run
不是必需的。 Task.Run
只应用于从UI线程推送CPU绑定工作,并且您的工作受I / O约束。
现在,在代码(未显示)中您曾经拥有并行性的点,您可以使用Task.WhenAll
使其并发。所以而不是:
Parallel.ForEach(sequence, x => MyAsync(x));
你应该这样做:
var tasks = sequence.Select(x => MyAsync(x));
await Task.WhenAll(tasks);
答案 1 :(得分:0)
要检测Task
期间Task.Run
个对象中是否存在未处理的异常,您应该处理TaskScheduler.UnobservedTaskException
事件。
如果Task
中包含的代码的其他部分中存在异常,则会识别它们及其来源。