0

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
i3arnon
  • 113,022
  • 33
  • 324
  • 344
Balraj Singh
  • 3,381
  • 6
  • 47
  • 82

2 Answers2

4

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));
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • Basically my aim was to create Read and Write File IO operation using Data-flow in a thread-safe manner. As instead of read write lock I can use ConcurrentExclusiveSchedulerPair which would save me from side effects of using locks in a good way. Please suggest if its possible. – Balraj Singh Dec 22 '15 at 02:56
  • @BalrajSingh you can use both for read and write operations. But you can never guarantee that the task that inserted the item will be the one reading it. If that's your goal you probably need multiple data structures. – i3arnon Dec 22 '15 at 05:48
  • What do you mean by multiple data structure. Can you please elaborate. – Balraj Singh Dec 22 '15 at 06:19
  • @BalrajSingh a block for each task for example. Or a concurrent queue. – i3arnon Dec 22 '15 at 08:07
  • Sure I did that. Its working great. But after posting the data execution of the block happens after some pause. I understand that it is because TransformBlock is expecting for more data. for that I also added TransformBlock.Complete() line after posting but the problem remains the same. Can you please tell me what should i do to make the execution immediately. – Balraj Singh Dec 22 '15 at 08:47
  • Yes I have already done that. Still there is a pause before it hits Action Block after posting data. – Balraj Singh Dec 22 '15 at 09:25
  • @BalrajSingh Then it's probably just the time it takes. – i3arnon Dec 22 '15 at 10:23
0

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.

Rob
  • 26,989
  • 16
  • 82
  • 98