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 个 Await
caluses,但是,它们不会相互冲突,或者可能导致数据丢失吗?
我使用此扩展执行了 FindAsync 并获取了我的数据,因此它可以正常工作,但测试不能保证 100% 的死锁发生。
我非常想知道为什么或为什么不。
谢谢。
这会导致死锁吗?是的,但不是因为你列出的原因。
如果有人决定同步调用您的方法并在返回的 Task
上使用 Wait
或 Result
,他们 可能 出现死锁.正是出于这个原因,SO 到处都是 "why does my task never complete" 个问题。在 GetResultsFromFindAsync
中等待的所有 Task
上使用 ConfigureAwait(false)
来保护自己。
话虽如此,如果 GetResultsFromFindAsync
始终异步消费,则没有问题。
关于只有一个 "single await
clause" - 这实际上不是真的。您正在 GetResultsFromFindAsync
实现中 await
ing 由 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
我正在尝试删除正在编写的代码。
有一段代码,在每个 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 个 Await
caluses,但是,它们不会相互冲突,或者可能导致数据丢失吗?
我使用此扩展执行了 FindAsync 并获取了我的数据,因此它可以正常工作,但测试不能保证 100% 的死锁发生。
我非常想知道为什么或为什么不。 谢谢。
这会导致死锁吗?是的,但不是因为你列出的原因。
如果有人决定同步调用您的方法并在返回的 Task
上使用 Wait
或 Result
,他们 可能 出现死锁.正是出于这个原因,SO 到处都是 "why does my task never complete" 个问题。在 GetResultsFromFindAsync
中等待的所有 Task
上使用 ConfigureAwait(false)
来保护自己。
话虽如此,如果 GetResultsFromFindAsync
始终异步消费,则没有问题。
关于只有一个 "single await
clause" - 这实际上不是真的。您正在 GetResultsFromFindAsync
实现中 await
ing 由 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