Balraj Singh
Balraj Singh

Reputation: 3471

Implementation issue with TransformBlock using TPL Dataflow

I am learning TPL dataflow. I tried creating a sample where I am posting some values from different Tasks and expecting the result back in the same Task to process it further. But the result is coming wrong. Following is my code. Let me know what wrong I am doing and how to resolve it.

static void Main(string[] args)
{

    var transBlock = new TransformBlock<int, int>
       (
           n =>
           {
               Thread.Sleep(1000);

               return (n*2);
           }
       );

    new Task(() => 
    {

       var result = transBlock.Post(2);
       var val = transBlock.Receive();

        Console.WriteLine(string.Format("double for 2 is {0}", val));
    }).Start();

    new Task(() =>
    {

        var result = transBlock.Post(3);
        var val = transBlock.Receive();

        Console.WriteLine(string.Format("double for 3 is {0}", val));
    }).Start();

    new Task(() =>
    {

        var result = transBlock.Post(4);
        var val = transBlock.Receive();

        Console.WriteLine(string.Format("double for 4 is {0}", val));
    }).Start();

    new Task(() =>
    {

        var result = transBlock.Post(5);
        var val = transBlock.Receive();

        Console.WriteLine(string.Format("double for 5 is {0}", val));
    }).Start();

    new Task(() =>
    {

        var result = transBlock.Post(6);
        var val = transBlock.Receive();

        Console.WriteLine(string.Format("double for 6 is {0}", val));
    }).Start();

    new Task(() =>
    {

        var result = transBlock.Post(7);
        var val = transBlock.Receive();

        Console.WriteLine(string.Format("double for 7 is {0}", val));

    }).Start();

    Console.ReadLine();
}

Result varies each time but once it came like this:

double for 5 is 8
double for 4 is 6
double for 3 is 4
double for 2 is 10
double for 6 is 12
double for 7 is 14

Upvotes: 0

Views: 218

Answers (2)

i3arnon
i3arnon

Reputation: 116558

That's not how TPL Dataflow works.

TPL Dataflow is an actor framework. You create a block, you tell it what to do, you post items into it and it executes the operation for each item one after the other (possibly concurrently) and then outputs the results. If you have multiple blocks then you can chain them one together and form a pipeline.

The block doesn't know who posted which item into it. There's no reason to expect the result to be returned to the matching task.

If you want to keep track of the input and output you can return a tuple of the input and output together:

var transBlock = new TransformBlock<int, Tuple<int,int>>(async n =>
{
    await Task.Delay(1000)
    return Tuple.Create(n, n * 2);
});


var tuple = transBlock.Receive();
Console.WriteLine(string.Format("double for {0} is {1}", tuple.Item1, tuple.Item2));

Upvotes: 4

Rob
Rob

Reputation: 27367

I do not know if TPL Dataflow is FIFO, but even if it is, your code has a race condition.

Consider just these two:

new Task(() => 
{

   var result = transBlock.Post(2);
   var val = transBlock.Receive();

    Console.WriteLine(string.Format("double for 2 is {0}", val));
}).Start();

new Task(() =>
{

    var result = transBlock.Post(3);
    var val = transBlock.Receive();

    Console.WriteLine(string.Format("double for 3 is {0}", val));
}).Start();

These tasks may or may not execute on separate threads. However, if they do, it's possible for the second task to post 3, and then pass the context to the first task, which posts 2 and receives 3.

Upvotes: 0

Related Questions