简体   繁体   English

使用BlockingCollection时出现死锁 <T> 和TPL数据流一起

[英]Deadlocks when using BlockingCollection<T> and TPL dataflow together

I've written a sample test that replicates the issue. 我写了一个复制问题的示例测试。 This is not my actual code, I've tried to write a small repro. 这不是我的实际代码,我试着写一个小的repro。 If you increase the bounding capacity to the number of iterations effectively giving it no bounding it does not deadlock and if you put the max parallelism to a small number like 1 it does not deadlock. 如果你将边界容量增加到迭代次数有效地给它没有边界,它就不会死锁,如果你把max parallelism放到像1这样的小数字,它就不会死锁。

Again, I know the code below is not great but the code I actually found this in was much larger and hard to understand. 同样,我知道下面的代码不是很好,但我实际发现的代码更大,难以理解。 Basically there was a blocking object pool of connections to a remote resource and several of the blocks in the flow used the connection. 基本上,存在与远程资源的连接的阻塞对象池,并且流中的若干块使用了连接。

Any ideas on how to solve this? 关于如何解决这个问题的任何想法? At first glance it appears to be a problem with dataflow. 乍一看,它似乎是数据流的问题。 When I break to take a look at the threads I see many threads blocked on Add and 0 threads blocked on take. 当我打破看看线程时,我看到许多线程被阻塞在Add和0线程被阻塞。 There are several items in the addBlocks outbound queue that have not yet propagated to the takeblock so it's stuck or deadlocked. addBlocks出站队列中有几个项尚未传播到takeblock,因此它被卡住或死锁。

    var blockingCollection = new BlockingCollection<int>(10000);

    var takeBlock = new ActionBlock<int>((i) =>
    {
        int j = blockingCollection.Take();

    }, new ExecutionDataflowBlockOptions()
           {
              MaxDegreeOfParallelism = 20,
              SingleProducerConstrained = true
           });

    var addBlock = new TransformBlock<int, int>((i) => 
    {
        blockingCollection.Add(i);
        return i;

    }, new ExecutionDataflowBlockOptions()
           {
              MaxDegreeOfParallelism = 20
           });

    addBlock.LinkTo(takeBlock, new DataflowLinkOptions()
          {
             PropagateCompletion = true
          });

    for (int i = 0; i < 100000; i++)
    {
        addBlock.Post(i);
    }

    addBlock.Complete();
    await addBlock.Completion;
    await takeBlock.Completion;

TPL Dataflow wasn't meant to be used with code that is blocking a lot, and I think this issue stems from that. TPL Dataflow并不适用于阻塞很多的代码,我认为这个问题源于此。

I couldn't figure out what exactly is going on, but I think a solution would be to use a non-blocking collection. 我无法弄清楚究竟发生了什么,但我认为解决方案是使用非阻塞集合。 Conveniently, Dataflow provides you with one in the form of BufferBlock . 方便的是,Dataflow以BufferBlock的形式为您提供了一个。 With that, your code would look like this: 有了它,您的代码将如下所示:

var bufferBlock = new BufferBlock<int>(
    new DataflowBlockOptions { BoundedCapacity = 10000 });

var takeBlock = new ActionBlock<int>(
    async i =>
    {
        int j = await bufferBlock.ReceiveAsync();
    }, new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = 20,
        SingleProducerConstrained = true
    });

var addBlock = new TransformBlock<int, int>(
    async i =>
    {
        await bufferBlock.SendAsync(i);
        return i;
    }, new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = 20
    });

Although I find the whole design of your code suspicious. 虽然我发现你的代码的整个设计是可疑的。 If you want to send some additional data along with the normal result of a block, change the type of the output of that block to a type that includes that additional data. 如果要发送一些其他数据以及块的正常结果,请将该块的输出类型更改为包含该附加数据的类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM