Search code examples
c#task-parallel-librarytransformfreezetpl-dataflow

C# TransformBlock doesn't run even after calling "Complete()"


I've got this code snippet, trying to use TransformBlock to start a code execution, as below:

public static void Main(string[] args)
{
    var multiplyBlock = new TransformBlock<int, int>(x => x * 2);
    var additionBlock = new TransformBlock<int, int>(x => x + 2);
    multiplyBlock.LinkTo(additionBlock, new DataflowLinkOptions { PropagateCompletion = true });
    multiplyBlock.Post(3);
    additionBlock.Completion.ContinueWith(x => Console.WriteLine(x));

    multiplyBlock.Complete();
    additionBlock.Completion.Wait();
}

But when I run this code, it hangs and prints nothing. I tried to debug it, I found all code lines have finished but at the end of function, the program hangs. So what's happening here, and how to fix it?

Thanks.


Solution

  • You need to use an ActionBlock to consume the output from the TransFormBlock, like so:

    public static void Main(string[] args)
    {
        var multiplyBlock = new TransformBlock<int, int>(x => x * 2);
        var additionBlock = new TransformBlock<int, int>(x => x + 2);
        var outputBlock   = new ActionBlock<int>(Console.WriteLine);
    
        multiplyBlock.LinkTo(additionBlock, new DataflowLinkOptions { PropagateCompletion = true });
        additionBlock.LinkTo(outputBlock,   new DataflowLinkOptions { PropagateCompletion = true });
    
        multiplyBlock.Post(3);
    
        multiplyBlock.Complete();
        outputBlock.Completion.Wait();
    }
    

    If you just try to use TransformBlock nothing happens because there's nothing to consume all the output.

    Think of it this way: a TransformBlock has an input and an output, so the output must be consumed somehow. An ActionBlock only has an input, and it will be called repeatedly with the output from a TransformBlock. (This is somewhat of an oversimplification, but it should help you understand the wiring.)

    In the example above, the output of the multiply block is wired to the input of the addition block, and the output of the addition block is wired to the input of the output block.

    The input to the multiply block must come from somewhere, and here it comes from the call to multiplyBlock.Post(3);.

    (Actually, if you want to consume the output of a TransformBlock explicitly, you can do so by calling TransformBlock.Receive() (or one of the other receive methods) in a loop to process the data, but this is not necessary since its much easier to use an ActionBlock to consume the transformed data.)

    This documentation may be useful to you.