【问题标题】:Why make an awaitable synchronous doesn't work in a separate method?为什么使等待同步不能在单独的方法中工作?
【发布时间】:2019-11-21 14:44:15
【问题描述】:

我想让异步方法同步运行。

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

所以我有我的异步方法

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();
}

谁能解释一下这种二分法?

编辑:

由于问题比较早,所以我澄清一下这个问题的最终目的。

问题不在于如何解决同步与异步反模式。我已经有一个很好的解决方案,我会在问题的第一部分解释它。

此外,还有一个问题(Call async method from sync action methods: Task.Run or ConfigureAwaits(false))解释了解决方案的优缺点等。

问题在于理解为什么这两行代码

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

“在线”工作,但如果我将它们放在不同的方法中(在另一个库中),它们就不再工作了。

请在此处查找 MVCE:https://github.com/cghersi/UWPExamples/tree/master/SyncAntiPattern

【问题讨论】:

  • 它可能正在吞下一个异常——它会在等待时被抛出,而不是在你那里的 try/catch 中,如果我记得的话,而且由于你的方法是无效的,这就是杀死它的原因
  • 你就不能task.Wait()吗?
  • 这段代码没有任何意义。让我们打个比方。假设您从事递送包裹的业务。工作流程应该是“在我们等待卡车可用时,寻找其他工作要做”。但是你写的工作流程是“雇佣一个工作人员,他写一个待办事项清单,说在你等待卡​​车可用时找到其他工作要做,然后雇佣另一个工作人员来做那个清单上的事情,并且然后再雇一个工人看前两个工人,直到他们完成为止。”这些都不能让卡车更快地返回;它浪费工人。
  • @CristianoGhersi 您是否可以发布问题的完整示例,以便我们可以将其复制到项目中并运行它并查看行为?

标签: c# asynchronous async-await task


【解决方案1】:

我已经有一个很好的解决方案,我会在问题的第一部分解释它。

你有one hack。就像所有 hack 一样,它不会在任何地方都有效。

小改进:使用GetAwaiter().GetResult() 代替Wait();这将避免 AggregateException 包装器出现故障。

问题在于理解为什么这两行代码“内联”工作,但如果我将它们放在不同的方法中(在另一个库中),它们就不再工作了。

区别在于AsyncMethod 的调用位置。在有效的代码中(避免死锁),AsyncMethod 是从 Task.Run 内部调用的,即在线程池线程上调用。因此,它不会捕获其上下文并避免deadlock。在不起作用的代码中,从UI线程调用AsyncMethod,然后线程池线程只是用于(异步)等待任务完成;所以AsyncMethod 捕获了 UI 上下文,你又回到了死锁状态。

【讨论】:

  • 你成就了我的一天,伙计!是的,我修改了 MVCE,所以现在它可以完美运行,诀窍是将 Func 传递给我的 Sync() 方法,以便它完全按照你说的那样执行!顺便说一句,我知道这是一个 hack,但生活有时远非理想...... ;)
  • 感谢您链接到该博客文章!我知道事情陷入僵局,但从未真正理解为什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 2019-02-08
相关资源
最近更新 更多