簡體   English   中英

在 Linq 表達式中使用 `.Result` 調用異步方法會導致死鎖

[英]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()

參考Async/Await - 異步編程的最佳實踐

總結第二條准則,您應該避免混合異步和阻塞代碼。 混合的異步和阻塞代碼會導致死鎖、更復雜的錯誤處理和上下文線程的意外阻塞。 本指南的一個例外是控制台應用程序的 Main 方法,或者——如果您是高級用戶——管理部分異步代碼庫。

有時,正如您已經發現的那樣,簡單的方法是遍歷列表並正確等待異步函數

例如

var invalidObjects = //...

foreach(var x in list){
    if(!(await _service.IsValidAsync(x)))
        invalidObjects.Add(x);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM