【问题标题】:blocking main thread on async/await在异步/等待上阻塞主线程
【发布时间】:2016-06-16 03:18:04
【问题描述】:

我正在尝试使我的基础存储库类异步,但遇到了一些麻烦。我在我的 C# 应用程序中使用 Dapper ORM。

基础方法

protected async Task<List<T>> Read<T>(CommandDefinition cmd) {
    using(SqlConnection myCon = new SqlConnection(Config.DBConnection)) {
        await myCon.OpenAsync();

        IEnumerable<T> results = await myCon.QueryAsync<T>(cmd);

        List<T> retVal = results.ToList();

        myCon.Close();

        return retVal;
    }
}

调用方法

public List<Category> GetAllActiveCategories(Guid siteGuid) {
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result;
}

在我看来,一切都井井有条。我有用 async 关键字装饰的方法声明。我正在等待异步方法。

我遇到的问题是await myCon.OpenAsync(); 上的线程阻塞。这是我第一次尝试使用 async 和 await,所以我确定我做错了什么,但这并不明显。请帮忙!

【问题讨论】:

  • @KirillShlenskiy 我认为您正在做某事。我将进行大部分编辑以显示我正在使用的阻止它的方法之一。
  • 既然您已经发布了消费方面,我很有信心。转发我的 cmets 作为答案。

标签: c# .net asynchronous async-await dapper


【解决方案1】:

发布的阅读代码很好。问题出在消费代码中。如果您在调用链中返回的Task 或其前身Task 上调用Wait() 或Result,则通常会与async 发生死锁。

在这些情况下,一般建议适用:don't block on async code。一旦您开始使用async/await,您应该在整个调用链中使用async/await。

所以,你的调用方法变成了

public Task<List<Category>> GetAllActiveCategoriesAsync(Guid siteGuid) {
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid);
}

... 或

public async Task<List<Category>> GetAllActiveCategoriesAsync(Guid siteGuid) {
    List<Category> result = await base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid);

    // Do something.

    return result;
}

【讨论】:

  • 我唯一的问题是如何在不阻塞的情况下访问最顶层的结果
  • @fizch,不幸的是,异步有一种“毒化”你的代码的方式——你必须将 整个 链重写为async。当然,如果您使用的是同步调用链,您始终可以在特定情况下使用老式的阻塞方法。或者,您可以通过在 Read 方法中等待的所有 Tasks 上使用 ConfigureAwait(false) 来解决您的问题,但是,尽管这样做是最佳实践,但故意阻止 Tasks 仍然是一种反模式,应该避免。
  • 但我离题了,因为我不再回答原来的问题。
【解决方案2】:

罪魁祸首是:

return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result;

正如基里尔所说,任何时候你在一个任务上使用.Wait() 或.Result,你都会同步阻塞。你需要做的是:

public Task<List<Category>> GetAllActiveCategories(Guid siteGuid) {
    return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid);
}

这会将一个Task返回给this方法的调用方法,依此类推……它必须是“一直向上”的异步。

如果此代码的顶级使用者是 ASP.NET,那就没问题了。只需返回一个Task&lt;IActionResult&gt;(或包装在任务中的适当返回类型),框架就会为您整理出awaiting。

如果您正在编写控制台应用程序或无法使其“一直异步”,则必须阻止 .Result 或将您的方法设置为 async void 并使用 await。可悲的是,这两个都不是一个很好的解决方案。从某种意义上说,Async/await 非常激进,您确实必须在整个堆栈中使用它。

【讨论】:

  • @fizch 添加了一点解释。基里尔的回答(和链接)也是很好的信息!
猜你喜欢
  • 2021-12-12
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
  • 2014-08-10
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
相关资源
最近更新 更多