When developing a parallel application, you will often have situations where a task must be completed before the main thread can continue processing. The Task Parallel Library includes several methods that allow you to wait for one or more parallel tasks
to complete. This recipe will cover two such methods: Task.Wait()
and Task.WaitAll()
.
In this recipe we will be creating three tasks, all of which read in the text classic books and produce a word count
. After we create the first task, we will wait for it to complete using Task.Wait()
, before starting the second and third task. We will then wait for both the second and third tasks to complete using Task.WaitAll()
before writing a message to the console.
Let's create a Console
application that demonstrates how to wait for task
completion.
Launch Visual Studio 2012.
Start a new project using the C# Console Application project template, and assign
WordCount
as the Solution name.Add the following
using
statements at the top of yourProgram
class:using System; using System.Linq; using System.Net; using System.Threading.Tasks;
In the
Main
method of theProgram
class, add a character array containing the basic punctuation marks. We will use this array instring.Split()
to eliminate punctuation marks. Also add a stringconstant
for theuser-agent
header of theWebClient
.char[] delimiters = { ' ', ',', '.', ';', ':', '-', '_', '/', '\u000A' }; const string headerText = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)";
OK, now let's create our first task. This task will use
WebClient
to read the Origin of Species by Darwin, and get its word count. Enter the following code in theMain
method of theProgram
class just below the previous statement:var task1 = Task.Factory.StartNew(() => { Console.WriteLine("Starting first task."); var client = new WebClient(); client.Headers.Add("user-agent", headerText); var words = client.DownloadString(@"http://www.gutenberg.org/files/2009/2009.txt"); var wordArray = words.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); Console.WriteLine("Origin of Species word count: {0}", wordArray.Count()); } );
Now, just below the previous task, write the following statements to wait on the task, and write a message to the
Console
application:task1.Wait(); Console.WriteLine("Task 1 complete. Creating Task 2 and Task 3.");
Below the previous statement, enter the code to create the second and third tasks. These tasks are very similar to the first task.
var task2 = Task.Factory.StartNew(() => { Console.WriteLine("Starting second task."); var client = new WebClient(); client.Headers.Add("user-agent", headerText); var words = client.DownloadString(@"http://www.gutenberg.org/files/16328/16328-8.txt"); var wordArray = words.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); Console.WriteLine("Beowulf word count: {0}", wordArray.Count()); }); var task3 = Task.Factory.StartNew(() => { Console.WriteLine("Starting third task."); var client = new WebClient(); client.Headers.Add("user-agent", headerText); var words = client.DownloadString(@"http://www.gutenberg.org/files/4300/4300.txt"); var wordArray = words.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); Console.WriteLine("Ulysses word count: {0}", wordArray.Count()); });
Finally, let's use
Task.WaitAll()
to wait for the second and third task to complete, then prompt the user to exit the program.Task.WaitAll()
takes an array oftask
as its parameter, and can be used to wait for any number of tasks to complete.Task.WaitAll(task2,task3); Console.WriteLine("All tasks complete."); Console.WriteLine("Press <Enter> to exit."); Console.ReadLine();
In Visual Studio 2012, press F5 to run the project. You should see output similar to the following screenshot. Note that the exact order of the last few lines of text may still vary depending on the execution order of the second and third tasks.
Although Task.Wait()
and Task.WaitAll()
are fairly self-explanatory, both have several overloads that offer different functionalities.
Task.Wait()
can take either an Int32
or TimeSpan
parameter to specify a specific period of time to wait. It can also accept a CancellationToken
token parameter for cancellation, which will be covered later in the chapter.
Task.WaitAll()
always takes an array of Task
as its first parameter, and has a second parameter which can be an Int32
or TimeSpan
as in Task.Wait
.
Another useful method not shown in the recipe is Task.WaitAny()
. WaitAny
is very similar to WaitAll
, except that it waits for only one Task
in the array of Task
to complete. The first Task
of Task
array to finish, completes the wait condition, and execution of the main thread is allowed to move forward.
It is important to note that when you call one of the Wait
methods, the runtime will check to see if the task you are waiting on has started executing. If task
has started executing, then the thread that called Wait
will block until task
has finished executing. However, if task
has not started running, then the runtime may execute the task using the thread that calls Wait
.
The various overloads and behaviors of Task.Wait
, Task.WaitAll
, and Task.WaitAny
are shown in the following table: