【问题标题】:Can BlockingCollection.GetConsumingEnumerable Deadlock可以 BlockingCollection.GetConsumingEnumerable 死锁
【发布时间】:2020-11-13 14:44:12
【问题描述】:

假设我有一个异步构建器方法,并且我需要将一组 db 模型转换为一组 UI 模型,我编写了下面的代码(例如简化)。

阅读this medium article 后,代码被重构为一直冒泡 ASYNC,但我仍然想知道

  1. collection.GetConsumingEnumerable()阻塞,导致死锁,类似于.Wait.Result
  2. 在异步方法中流式传输项目的首选方式是什么
    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();
    }

【问题讨论】:

标签: c# async-await ienumerable


【解决方案1】:

将collection.GetConsumingEnumerable()阻塞,导致死锁,类似于.Wait或.Result

没有。 the classic deadlock有两个部分:

  1. 一次只允许一个线程的上下文(通常是 UI SynchronizationContext 或 ASP.NET pre-Core SynchronizationContext)。
  2. 阻止使用await 的代码(其中uses that context 以完成async 方法)。

GetConsumingEnumerable 是一个阻塞调用,但它不会阻塞异步代码,所以那里没有经典死锁的危险。更一般地说,使用这样的队列会在两段代码之间插入某种“障碍”。

在异步方法中流式传输项目的首选方式是什么

现代代码应该使用IAsyncEnumerable&lt;T&gt;

但是,如果您尚未使用该平台,则可以将队列用作“障碍”。不过,您不会想使用BlockingCollection&lt;T&gt;,因为它是为同步生产者和同步消费者设计的。对于异步生产者/消费者,我建议使用 Channels。

【讨论】:

  • 谢谢斯蒂芬,你的博客上有很多很棒的信息。当您有时间时,我很想看到一个包含您的三个示例的博客:异步枚举、队列和通道。
  • 在阻塞收集说明上,我确实遇到了提前完成的问题。我必须添加 await Task.WhenAll(tasks); 来阻止,直到所有添加完成。你指的是这个问题吗?
  • @BrantleyBlanchard:不;我的意思是 GetGenericDevices 现在是一种同步方法,它(可能)在 UI 线程上调用。因此,foreach 在其可枚举对象上只会阻塞 UI。您可以改用 Channels,其中消费模式包括 awaiting 以提供更多项目 - 这样,UI 可以保持响应。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多