This recipe shows how to handle many asynchronous tasks running simultaneously. We will learn how to be notified effectively when all tasks are complete or any of the running tasks have to finish their work.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

//
// Running tasks in parallel
//
// This recipe shows how to handle many asynchronous tasks running
// simultaneously. We will learn how to be notified effectively when all tasks
// are complete or any of the running tasks have to finish their work.
//
// How it works...
//
// When the program starts, we create two tasks, and then with the help of the
// Task.WhenAll method, we create a third task, which will complete after all
// the tasks are complete. The resulting task provides us with an answers array,
// where the first element holds the first task's result, the second element
// holds the second result, and so on.
//
// Then, we create another list of tasks and wait for any of those tasks to
// complete with the Task.WhenAny method. After we have one finished task, we
// remove it from the list and continue to wait for the other tasks to complete
// until the list is empty. This method is useful to get the tasks' completion
// progress or to use timeout while running the tasks. For example, we wait for
// a number of tasks and one of those tasks is counting a timeout. If this task
// completes first, we just cancel those tasks that are not completed yet.
//

namespace Chapter4.Recipe8
{
    class Program
    {
        static void Main(string[] args)
        {
            var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
            var secondTask = new Task<int>(() => TaskMethod("Second Task", 2));

            var whenAllTask = Task.WhenAll(firstTask, secondTask);

            whenAllTask.ContinueWith(t =>
                Console.WriteLine("The first answer is {0}, the second is {1}",
                    t.Result[0], t.Result[1]),
                TaskContinuationOptions.OnlyOnRanToCompletion
            );
            firstTask.Start();
            secondTask.Start();

            Thread.Sleep(TimeSpan.FromSeconds(4));

            var tasks = new List<Task<int>>();
            for (int i = 1; i < 4; i++)
            {
                int counter = i;

                var task = new Task<int>(() =>
                    TaskMethod(string.Format("Task {0}", counter), counter)
                );

                tasks.Add(task);
                task.Start();
            }

            while (tasks.Count > 0)
            {
                var completedTask = Task.WhenAny(tasks).Result;

                tasks.Remove(completedTask);

                Console.WriteLine("A task has been completed with result {0}.",
                    completedTask.Result);
            }

            Thread.Sleep(TimeSpan.FromSeconds(1));
        }

        static int TaskMethod(string name, int seconds)
        {
            Console.WriteLine(
                "Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsThreadPoolThread);

            Thread.Sleep(TimeSpan.FromSeconds(seconds));

            return 42 * seconds;
        }
    }
}