有条件地等待任务

Waiting for a Task conditionally

对于 return 是 Task<bool>async 方法,我需要在方法完成时采取一些措施。异步方法如下所示:

async Task<bool> EntryExists(string entry)
{
  return await Task.Run(() => call_that_returns_bool());
}

我调用它并为其附加一个延续任务以执行后续操作:

EntryExists(my_entry).ContinueWith(t =>
{
  if(t.Result) ...
});

但是,我现在需要有条件地等待链式任务完成。意味着取决于参数,我要么需要立即 return 给调用者,要么等到任务完成。我将上面的代码更改为如下所示:

var T = EntryExists(my_entry).ContinueWith(t =>
{
  if(t.Result) ...
});

if(wait) T.Wait(); //wait is my parameter

运行 这样,当 wait 参数为 true 时,代码永远卡在 T.Wait() 处,就好像 T 从未启动过一样。然后我尝试了以下操作:

var T = EntryExists(my_entry).ContinueWith(t =>
{
  if(t.Result) ...
});

T.Start();
if(wait) T.Wait();

据此它告诉我

Start may not be called on a continuation task

我知道我缺少一些基本的东西,但是在过去 15 个小时的编码之后,我的大脑并没有多大帮助。有人可以指出我需要做什么吗?

You shouldn't block on async code。简而言之,您很可能会阻塞异步方法需要 return 的线程(在本例中,您是,因为它是 UI 线程)。解决方案是一直使用 async-await,而不是尝试同步 运行 它。如果您绝对必须提供同步方法,那么至少提供尽可能简单的包装器,而不是混合使用 ContinueWith 和 async-await.

你的外部调用可以重写为:

async Task<{something}> MakeCallAndContinue()
{
    try
    {
        await EntryExists(my_entry);
        // additional continuation code
    }
    catch (Exception e)
    {
        // handle in some way
    }
}
var task = MakeCallAndContinue();
if (wait) await task;

想要开始一项任务然后不等待它是不寻常的,如果 wait 为假,这就是您正在做的事情。我放在上面代码中的错误处理程序是为了确保您不会将异常抛出到未等待的任务上。如果你这样做,那么它会被扔到别的地方,如果你没有声明一个全局处理程序,它可能会终止你的进程。

您将无法按原样在 WPF 命令中使用上面的命令,因为它是异步的。但是,您可以使用异步 WPF 命令处理程序,如 here 所述。如果您确实想同步执行它,那么您需要调用 .Wait(),但正如 Stephen Cleary 在我的第一个 link 中解释的那样,您必须一直对所有等待的任务使用 ConfigureAwait(false) down 以防止其中之一试图 return 到占用的 UI 线程,并死锁。