Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ContinueWith chaining not working as expected

I have this example code:

    static void Main(string[] args) {
        var t1 = Task.Run(async () => {
            Console.WriteLine("Putting in fake processing 1.");
            await Task.Delay(300);
            Console.WriteLine("Fake processing finished 1. ");
        });
        var t2 = t1.ContinueWith(async (c) => {
            Console.WriteLine("Putting in fake processing 2.");
            await Task.Delay(200);
            Console.WriteLine("Fake processing finished 2.");
        });
        var t3 = t2.ContinueWith(async (c)  => {
            Console.WriteLine("Putting in fake processing 3.");
            await Task.Delay(100);
            Console.WriteLine("Fake processing finished 3.");
        });
        Console.ReadLine();
    }

The console output baffles me:

  • Putting in fake processing 1.
  • Fake processing finished 1.
  • Putting in fake processing 2.
  • Putting in fake processing 3.
  • Fake processing finished 3.
  • Fake processing finished 2.

I am trying to chain the tasks so they execute one after another, what am I doing wrong? And I can't use await, this is just example code, in reality I am queueing incoming tasks (some asynchronous, some not) and want to execute them in the same order they came in but with no parallelism, ContinueWith seemed better than creating a ConcurrentQueue and handling everythning myself, but it just doesn't work...

like image 221
Martin Sykora Avatar asked Sep 05 '14 17:09

Martin Sykora


1 Answers

Take a look at the type of t2. It's a Task<Task>. t2 will be completed when it finishes starting the task that does the actual work not when that work actually finishes.

The smallest change to your code to get it to work would be to add an unwrap after both your second and third calls to ContinueWith, so that you get out the task that represents the completion of your work.

The more idiomatic solution would be to simply remove the ContinueWith calls entirely and just use await to add continuations to tasks.

Interestingly enough, you would see the same behavior for t1 if you used Task.Factory.StartNew, but Task.Run is specifically designed to work with async lambdas and actually internally unwraps all Action<Task> delegates to return the result of the task returned, rather than a task that represents starting that task, which is why you don't need to unwrap that task.

like image 123
Servy Avatar answered Sep 16 '22 13:09

Servy