C#-WPF奇怪的异步行为

时间:2015-10-01 07:41:33

标签: c# .net wpf async-await relaycommand

我的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使我的异步方法停止...

有没有人有想法?

2 个答案:

答案 0 :(得分:3)

  

我发现这是因为我的异步内容在Parallel.For和Parallel.ForEach中,这使得它在结束前返回

并行性和异步性是两种不同的形式的并发性,它们并不能很好地协同工作。

当您拥有受多个核心受益的CPU限制算法时,应该使用并行性。当您有I / O绑定或事件触发的代码时,应该使用异步。

  

异步联系webservice,插入数据库,......

听起来像异步是适当的并发形式,而不是并行性。

正确应用异步,请从&#34;离开&#34; (即,执行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中包含的代码的其他部分中存在异常,则会识别它们及其来源。