Search code examples
c#asynchronousconcurrencytask-parallel-libraryconcurrent-programming

Ordering of tasks in TPL


If I have the following code

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication3
{
    class Program
    {
        static Task<int> GetSuperLargeNumber()
        {
            var main = Task.Factory.StartNew(() =>
                                                 {
                                                     Thread.Sleep(1000);
                                                     return 100;
                                                 });
            var second = main.ContinueWith(x => Console.WriteLine("Second: " + x.Result), TaskContinuationOptions.AttachedToParent);
            var third = main.ContinueWith(x => Console.WriteLine("Third: " + x.Result), TaskContinuationOptions.AttachedToParent);

            return main.ContinueWith(x  =>
                                             {
                                                 Task.WaitAll(second, third);
                                                 return x.Result;
                                             });
        }

        static void Main(string[] args)
        {
            GetSuperLargeNumber().ContinueWith(x => Console.WriteLine("Complete"));

            Console.ReadKey();
        }
    }
}

I want main to start first, then 2 dependencies can start after that which in parallel which are first and second. I want to then return a future with the value for the caller to add a continuation on. However i want to ensure second and third have run first. Is the code below the best way to achieve this? Seems kinda clunky


Solution

  • I'm not too familiar with TPL, but isn't this what ContinueWhenAll is for?

    static Task<int> GetSuperLargeNumber()
    {
        var main = Task.Factory.StartNew(() =>
                                             {
                                                 Thread.Sleep(1000);
                                                 return 100;
                                             });
        var second = main.ContinueWith(
            x => Console.WriteLine("Second: " + x.Result),
            TaskContinuationOptions.AttachedToParent);
        var third = main.ContinueWith(
            x => Console.WriteLine("Third: " + x.Result),
            TaskContinuationOptions.AttachedToParent);
    
        return Task.Factory.ContinueWhenAll(
            new[] { second, third },
            (twotasks) => /* not sure how to get the original result here */);
        }
    

    I don't know how to get main's result from the completed second and third (contained in twotasks), but maybe you can modify them to pass through the result.

    Edit: Or, as Alex points out, use

    Task.Factory.ContinueWhenAll(new[] { main, second, third }, (threetasks) => ...
    

    and read the result from threetasks[0].