c# mongodb FindAsync 扩展中可能出现死锁

c# mongodb possible deadlock in FindAsync extention

我正在尝试删除正在编写的代码。 有一段代码,在每个 FindAsync 中我们需要写:

using (var cursor = await SomeCollection.FindAsync(filter, options))
{
     while (await cursor.MoveNextAsync())
     {
          var batch = cursor.Current;
          foreach (var item in batch)
          {
              //do something
          }
     }
}

我想出了一个扩展。我的代码:

public static async Task<List<T>> GetResultsFromFindAsync<T>(this Task<IAsyncCursor<T>> find)
{
    List<T> result = new List<T>();
    using (var cursor = await find)
    {
        while (await cursor.MoveNextAsync())
        {
            var batch = cursor.Current;
            foreach (var item in batch)
            {
                result.Add(item);
            }
        }
    }

    return result;
}

现在,我只使用:

List<MyObject> lst = await SomeCollection.FindAsync(filter, options).GetResultsFromFindAsync();

问题是:它是否会导致死锁,因为在一个进程中涉及 2 个异步任务,具有单个 Await caluse。 我知道这个过程确实包含 2 个 Awaitcaluses,但是,它们不会相互冲突,或者可能导致数据丢失吗?

我使用此扩展执行了 FindAsync 并获取了我的数据,因此它可以正常工作,但测试不能保证 100% 的死锁发生。

我非常想知道为什么或为什么不。 谢谢。

这会导致死锁吗?是的,但不是因为你列出的原因。

如果有人决定同步调用您的方法并在返回的 Task 上使用 WaitResult,他们 可能 出现死锁.正是出于这个原因,SO 到处都是 "why does my task never complete" 个问题。在 GetResultsFromFindAsync 中等待的所有 Task 上使用 ConfigureAwait(false) 来保护自己。

话虽如此,如果 GetResultsFromFindAsync 始终异步消费,则没有问题。

关于只有一个 "single await clause" - 这实际上不是真的。您正在 GetResultsFromFindAsync 实现中 awaiting 由 FindAsync 返回的 Task,从而传播其完成和异常(如果有)。当你打电话

SomeCollection.FindAsync(filter, options).GetResultsFromFindAsync()

,你实际上在等待两个 Tasks(因为内部 Task 被外部 Task 等待)。如果 FindAsync 恰好抛出(异步),GetResultsFromFindAsync 返回的 Task 将自动转换为 Faulted 状态并在外部 [=13] 时重新抛出异常=] 正在等待。

总而言之,您编写的方法在技术上没有任何问题,尽管引入 ConfigureAwait(false) 不会有什么坏处。

编辑

综上所述,我个人会考虑将两个调用合并为一个,这样调用FindAsync就是GetResultsFromFindAsync的责任了。这是为了防止消费者重复使用 FindAsync 返回的游标,因为我预计以下操作会失败:

IAsyncCursor<T> cursor = await SomeCollection.FindAsync(filter, options);
List<MyObject> lst1 = await cursor.GetResultsFromFindAsync();
List<MyObject> lst2 = await cursor.GetResultsFromFindAsync(); // BOOM.

.NET 驱动程序已经为此类事物提供了扩展方法。 ToList、ToListAsync 和 ForEachAsync 都可用。

http://mongodb.github.io/mongo-csharp-driver/2.2/reference/driver/crud/reading/#iteration