为什么使等待的同步无法在单独的方法中工作?

时间:2019-07-11 20:14:25

标签: c# asynchronous async-await task

我想制作一个异步运行的异步方法。

是的,我知道这是一个不好的做法,但是有很多原因需要我们这样做,因此,为了这个问题,让我们假设我们必须这样做,并且没有其他方法可以达到最终结果。

所以我有异步方法

public static async Task AsyncMethod();

如果我这样称呼:

public static void SyncMethod()
{
  Task task = Task.Run(async () => { await AsyncMethod(); });
  task.Wait();
}

一切正常。

但是,如果我想概括我的方法并将此逻辑放在一个单独的方法中,放在一个实用程序库中(该库与UWP项目是一个单独的库),则该方法永远不会返回。这是使用无效方法的代码:

public static class Utilities
{
public static void Sync(this Task task)
{
    if (task == null)
        return;

    Task t = Task.Run(async () =>
    {
        try
        {
            await task;
        }
        catch (Exception e)
        {
            s_log.InfoFormat("Exception while running task {0} due to {1}", task.Id, e.Message);
        }
    });
    t.Wait();
}
}

然后调用该方法即可:

public static void SyncMethod()
{
  AsyncMethod().Sync();
}

有人可以解释这种二分法吗?

编辑:

由于对问题的早期评论,让我澄清这个问题的最终目的。

问题不在于如何解决“同步与异步”反模式。我已经有一个很好的解决方案,我将在问题的第一部分对此进行解释。

此外,还有一个问题(here's the pull request)解释了解决方案的优缺点,等等。

问题是要理解为什么这两行代码

  Task task = Task.Run(async () => { await AsyncMethod(); });
  task.Wait();

是“在线”工作的,但是如果我将它们放在其他方法中(在另一个库中),它们将无法工作。

请在此处找到MVCE:Call async method from sync action methods: Task.Run or ConfigureAwaits(false)

1 个答案:

答案 0 :(得分:3)

  

我已经有了一个很好的解决方案,我在问题的第一部分对此进行了解释。

您有one hack。像所有黑客一样,它不会在任何地方都起作用。

较小的改进:使用GetAwaiter().GetResult()代替Wait();这样可以避免在失败情况下使用AggregateException包装器。

  

相反,问题是要理解为什么这两行代码“串联”工作,但是如果我将它们放在其他方法(在另一个库中)中,它们将不再起作用。

区别在于调用AsyncMethod的位置。在有效的代码中(避免死锁),AsyncMethod是在Task.Run内部调用的,即在线程池线程上。因此,它不捕获其上下文并避免使用deadlock。在无效的代码中,从UI线程调用AsyncMethod,然后仅使用线程池线程(异步地)等待任务完成;因此AsyncMethod捕获了UI上下文,而您又陷入了僵局。