There are several ways to solve the proposed problem.
There is a static Task.WaitAll
method that takes an array of tasks as arguments and blocks the calling thread until all these tasks are completed. In addition, there are overloads of this method, which take a wait timeout (and return a sign that the tasks completed at the specified time) and / or a wait cancellation token. There is also an overload with a parameter that takes a variable number of arguments (the params
):
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2); t1.Start(); t2.Start(); Task.WaitAll(t1, t2); Console.WriteLine("Tasks completed"); } private static void DoSomething2() { Console.WriteLine("Do something 2"); } private static void DoSomething1() { Console.WriteLine("Do something 1"); }
You should not confuse the Task.WaitAll
method with the Task.WaitAny
method, which is waiting for any of the tasks transferred.
If you can write using .NET 4.5, you can use the async/await
keywords to wait for tasks to complete asynchronously. The Task.WhenAll
method will create a task that will complete when all the tasks passed to it as arguments are completed.
The code below will be executed synchronously until execution reaches the await
instruction. At this moment, control will be transferred to the code that called the Foo
method, and the waiting for the execution of tasks will occur in another thread. Upon completion of the tasks, the control will be returned to the Foo
method and it will continue to work:
public static async void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2); t1.Start(); t2.Start(); await Task.WhenAll(t1, t2); Console.WriteLine("Tasks completed"); } private static void DoSomething2() { Console.WriteLine("Do something 2"); } private static void DoSomething1() { Console.WriteLine("Do something 1"); }
In addition, the task generated by the Task.WhenAll
method is a normal task, which means you can wait for it synchronously using the Task.Wait
method:
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2); t1.Start(); t2.Start(); Task.WhenAll(t1, t2).Wait(); Console.WriteLine("Tasks completed"); } private static void DoSomething2() { Console.WriteLine("Do something 2"); } private static void DoSomething1() { Console.WriteLine("Do something 1"); }
Finally, the task generated by the Task.WhenAll
method can be continued using the Task.ContinueWith
method. This method starts the task passed to it as an argument after the completion of the specified task. In the continuation task, with this, you can handle the results and exceptions of the tasks that were originally necessary to wait:
public static void Foo() { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2); t1.Start(); t2.Start(); var continuation = Task.WhenAll(t1, t2).ContinueWith( t => { Console.WriteLine("Tasks completed"); }); continuation.Wait(); } private static void DoSomething2() { Console.WriteLine("Do something 2"); } private static void DoSomething1() { Console.WriteLine("Do something 1"); }
PS Special thanks to @NickVolynkin for offering to ask a question and answer it yourself and @PavelMayorov for the idea of waiting through Task.WhenAll
.