[英]Calling an async method with `.Result` in a Linq expression causes deadlock
我們遇到了一個錯誤,我們必須使用異步方法驗證對象列表。 代碼的作者想把它塞進一個 Linq 表達式中,如下所示:
var invalidObjects = list
.Where(x => _service.IsValidAsync(x).Result)
.ToList();
驗證方法如下所示:
public async Task<bool> IsValidAsync(object @object) {
var validObjects = await _cache.GetAsync<List<object>>("ValidObjectsCacheKey");
return validObjects.Contains(@object);
}
這個小解決方案導致整個應用程序掛在await _cache.GetAsync
行上。 緩存是分布式緩存(redis)。 將 linq 更改為簡單的 foreach 並正確等待_service.IsValidAsync
,代碼運行無死鎖並且基本上是在瞬間。
我在基本層面上理解async-await
是如何工作的,但我無法理解為什么會發生這種情況,尤其是因為列表只有一個對象。
歡迎任何建議!
編輯:該應用程序在 .net Core 2.2 上運行,但發生問題的庫針對 .netstandard 2.0。 System.Threading.SynchronizationContext.Current
在死鎖時返回 null
EDIT2:改變緩存提供者(但仍然通過異步方法訪問它)也解決了這個問題,所以這個錯誤實際上可能在 redis 緩存客戶端中: https : //github.com/aspnet/Caching/blob/master /src/Microsoft.Extensions.Caching.StackExchangeRedis/RedisCache.cs
除了已經說明的混合 async-await 和阻塞調用(如.Result
或.Wait()
總結第二條准則,您應該避免混合異步和阻塞代碼。 混合的異步和阻塞代碼會導致死鎖、更復雜的錯誤處理和上下文線程的意外阻塞。 本指南的一個例外是控制台應用程序的 Main 方法,或者——如果您是高級用戶——管理部分異步代碼庫。
有時,正如您已經發現的那樣,簡單的方法是遍歷列表並正確等待異步函數
例如
var invalidObjects = //...
foreach(var x in list){
if(!(await _service.IsValidAsync(x)))
invalidObjects.Add(x);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.