可以BlockingCollection.GetConsumingEnumerable死锁
Can BlockingCollection.GetConsumingEnumerable Deadlock
假设我有一个异步生成器方法并且我需要将数据库模型集合转换为 UI 模型集合,我编写了下面的代码(例如简化)。
阅读 this medium article 后,代码重构为 bubble ASYNC,但我仍然想知道
- 会
collection.GetConsumingEnumerable()
阻塞,导致死锁,类似于.Wait
或.Result
- 在异步方法中流式传输项目的首选方式是什么
public IEnumerable<GenericDevice> GetGenericDevices(SearchRequestModel srModel)
{
var collection = new BlockingCollection<GenericDevice>(new ConcurrentBag<GenericDevice>());
Task.Run(() => QueueGenericDevices(collection, srModel));
foreach (var genericDevice in collection.GetConsumingEnumerable())
{
yield return genericDevice;
}
}
private async Task QueueGenericDevices(BlockingCollection<GenericDevice> collection, SearchRequestModel srModel)
{
var efDevices = _dbDeviceRepo.GetEfDevices(srModel);
var tasks = efDevices
.Select(async efDevice =>
{
var genericDevice = await BuildGenericDevice(efDevice, srModel);
collection.Add(genericDevice);
});
await Task.WhenAll(tasks);
collection.CompleteAdding();
}
will collection.GetConsumingEnumerable() block, leading to deadlocks, similar to .Wait or .Result
没有。 the classic deadlock 有两部分:
- 一次只允许一个线程的上下文(通常是 UI
SynchronizationContext
或 ASP.NET pre-Core SynchronizationContext
)。
- 阻止使用
await
的代码(uses that context 以完成 async
方法)。
GetConsumingEnumerable
是一个阻塞调用,但它不会阻塞异步代码,因此不存在经典死锁的危险。更一般地说,使用这样的队列会在两段代码之间插入某种“障碍”。
what is the preferred way to stream items in an async method
现代代码应该使用IAsyncEnumerable<T>
。
但是,如果您还没有使用该平台,则可以使用队列作为“障碍”。不过,您不会想使用 BlockingCollection<T>
,因为它是为同步生产者和同步消费者设计的。对于异步 producers/consumers,我推荐 Channels。
假设我有一个异步生成器方法并且我需要将数据库模型集合转换为 UI 模型集合,我编写了下面的代码(例如简化)。
阅读 this medium article 后,代码重构为 bubble ASYNC,但我仍然想知道
- 会
collection.GetConsumingEnumerable()
阻塞,导致死锁,类似于.Wait
或.Result
- 在异步方法中流式传输项目的首选方式是什么
public IEnumerable<GenericDevice> GetGenericDevices(SearchRequestModel srModel)
{
var collection = new BlockingCollection<GenericDevice>(new ConcurrentBag<GenericDevice>());
Task.Run(() => QueueGenericDevices(collection, srModel));
foreach (var genericDevice in collection.GetConsumingEnumerable())
{
yield return genericDevice;
}
}
private async Task QueueGenericDevices(BlockingCollection<GenericDevice> collection, SearchRequestModel srModel)
{
var efDevices = _dbDeviceRepo.GetEfDevices(srModel);
var tasks = efDevices
.Select(async efDevice =>
{
var genericDevice = await BuildGenericDevice(efDevice, srModel);
collection.Add(genericDevice);
});
await Task.WhenAll(tasks);
collection.CompleteAdding();
}
will collection.GetConsumingEnumerable() block, leading to deadlocks, similar to .Wait or .Result
没有。 the classic deadlock 有两部分:
- 一次只允许一个线程的上下文(通常是 UI
SynchronizationContext
或 ASP.NET pre-CoreSynchronizationContext
)。 - 阻止使用
await
的代码(uses that context 以完成async
方法)。
GetConsumingEnumerable
是一个阻塞调用,但它不会阻塞异步代码,因此不存在经典死锁的危险。更一般地说,使用这样的队列会在两段代码之间插入某种“障碍”。
what is the preferred way to stream items in an async method
现代代码应该使用IAsyncEnumerable<T>
。
但是,如果您还没有使用该平台,则可以使用队列作为“障碍”。不过,您不会想使用 BlockingCollection<T>
,因为它是为同步生产者和同步消费者设计的。对于异步 producers/consumers,我推荐 Channels。