Waiting For Tasks
Wait Methods
- Task class provide several overloads of
Wait
methods that enable you to wait for a task to finish. - Static
WaitAll
method let you wait for all of an array of tasks to finish. - Static
WaitAny
method let you wait for any of an array of tasks to finish.
Reasons For Waiting
Typically, you would wait for a task for one of these reasons:
- The main thread depends on the final result computed by a task.
- You have to handle exceptions that might be thrown from the task.
- The application may terminate before all tasks have completed execution. For example, console applications will terminate as soon as all synchronous code in Main (the application entry point) has executed.
Wait
Waits for the Task to complete execution.
Wait with No Args
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
taskA.Start();
taskA.Wait();
Wait with Timeout
Task taskA = Task.Run(() => DoComputation());
if(!taskA.Wait(100))
Console.WriteLine("The timeout interval elapsed");
Task taskA = Task.Run(() => DoComputation());
if(!taskA.Wait(TimeSpan.FromMilliseconds(50)))
Console.WriteLine("The timeout interval elapsed");
WaitAll
Waits for all of the provided Task objects to complete execution.
Code Syntax
Task[] tasks = new Task[3]
{
Task.Run(() => MethodA()),
Task.Run(() => MethodB()),
Task.Run(() => MethodC())
};
//Block until all tasks complete.
Task.WaitAll(tasks);
Code Example
private static Action<Object> PrintThreadInfo = (object input) => {
int i = (int)input;
if(i >= 2 && i <= 5)
{
throw new InvalidOperationException("Simulated Exception");
}
Console.WriteLine($"Task Id: {Task.CurrentId}, Input: {i}, TickCount: {Environment.TickCount}, ThreadId: {Thread.CurrentThread.ManagedThreadId}");
};
private static void WaitAllForTasks()
{
var tasks = new List<Task>();
for(int i = 0; i < 10; i++)
{
tasks.Add(Task.Factory.StartNew(PrintThreadInfo, i));
}
try
{
Task.WaitAll(tasks.ToArray());
}
catch(AggregateException e)
{
for(int j = 0; j < e.InnerExceptions.Count; j++)
{
Console.WriteLine(e.InnerExceptions[j].Message);
}
}
}
WaitAny
- Waits for any of the provided Task objects to complete execution.
- Returns the index of the completed task object in the tasks array.
Code Syntax
Task[] tasks = new Task[3]
{
Task.Run(() => MethodA()),
Task.Run(() => MethodB()),
Task.Run(() => MethodC())
};
//Block until any of tasks complete.
Task.WaitAny(tasks);
Code Example
private static void WaitForAnyTask()
{
Task[] tasks = new Task[5];
for (int i = 0; i <= 4; i++) {
int factor = i;
tasks[i] = Task.Run(() => Thread.Sleep(factor * 250 + 50));
}
int index = Task.WaitAny(tasks);
Console.WriteLine("Wait ended because task #{0} completed.", tasks[index].Id);
Console.WriteLine("Current Status of Tasks:");
foreach (var t in tasks)
Console.WriteLine("Task {0}: {1}", t.Id, t.Status);
}
WhenAll
Creates a task that will complete when all of the supplied tasks have completed.
Preferred over WaitAll
Code Syntax
Task[] tasks = new Task[3]
{
Task.Run(() => MethodA()),
Task.Run(() => MethodB()),
Task.Run(() => MethodC())
};
Task t = Task.WhenAll(tasks);
t.Wait();
Code Example
private static int failed = 0;
private static void WhenAllForTasks()
{
List<Task> tasks = PingWebsites();
Task t = Task.WhenAll(tasks);
try
{
t.Wait();
}
catch {}
if(t.Status == TaskStatus.RanToCompletion)
Console.WriteLine("All tasks completed");
else
Console.WriteLine("{0} tasks failed", failed);
}
private static List<Task> PingWebsites()
{
failed = 0;
string[] urls = {
"www.websitenotfound1.com",
"www.websitenotfound2.com",
"www.google.com"
};
var tasks = new List<Task>();
foreach(var url in urls)
{
tasks.Add(Task.Run(() => {
Console.WriteLine($"Url {url}, Thread Id: {Thread.CurrentThread.ManagedThreadId}");
var ping = new Ping();
try
{
var reply = ping.Send(url);
Console.WriteLine($"Url {url}, Thread Id: {Thread.CurrentThread.ManagedThreadId}, Status: {reply.Status}");
if(reply.Status != IPStatus.Success)
{
Interlocked.Increment(ref failed);
throw new TimeoutException($"Unable to reach {url}");
}
}
catch(PingException e)
{
Console.WriteLine($"Url {url}, Thread Id: {Thread.CurrentThread.ManagedThreadId}, Status: {e.Message}");
Interlocked.Increment(ref failed);
throw;
}
}));
}
return tasks;
}
WhenAny
- Creates a task that will complete when any of the supplied tasks have completed.
- Preferred over
WaitAny
- The returned task will always end in the
RanToCompletion
state even if the first task to complete ended in the Canceled or Faulted state.
private static void WhenAnyForTasks()
{
List<Task> tasks = PingWebsites();
Task t = Task.WhenAny(tasks);
try
{
t.Wait();
}
catch {}
if(t.Status == TaskStatus.RanToCompletion)
Console.WriteLine("Any task completed");
else
Console.WriteLine("{0} tasks failed", failed);
}