Why Tasks?
- Limitation of Threadpool
QueueUserWorkItem
- No Built in way to know when operation is completed.
- No way to return values
Tasks are introduced to overcome the above limitations. We need to note down below points to properly understand tasks.
- The Scheduler may use the current thread to execute the task
- Unhandled exception swallowed
Wait
/Result
will throwSystem.AggregateException
- .NET 4
TaskScheduler.UnobservedTaskException
- .NET 4.5 App Config
<ThrowUnobservedTaskExceptions enabled="true"/>
- Wait on Array of Tasks:
WaitAny
to wait on array of tasks returns index / cancellation exception
WaitAll
to wait on array of tasks returns true for success / false for time out - Task Schedulers:
Schedules all tasks onto GUI thread usingFromCurrentSynchronizationContext
Examples
Task Start
using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// Create a task and supply a user delegate by using a lambda expression.
Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
// Start the task.
taskA.Start();
// Output a message from the calling thread.
Console.WriteLine("Hello from thread '{0}'.",
Thread.CurrentThread.Name);
taskA.Wait();
}
}
// The example displays output like the following:
// Hello from thread 'Main'.
// Hello from taskA.
Task Creation Options
// TaskCreationOptions: Specifies flags that control optional behavior for the creation and execution of tasks
var task3 = new Task(() => MyLongRunningMethod(),
TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();
Task Continuation
using System;
using System.Threading.Tasks;
public class Example
{
public static async Task Main()
{
// Execute the antecedent.
Task<DayOfWeek> taskA = Task.Run( () => DateTime.Today.DayOfWeek );
// Execute the continuation when the antecedent finishes.
await taskA.ContinueWith( antecedent => Console.WriteLine("Today is {0}.", antecedent.Result) );
}
}
// The example displays output like the following output:
// Today is Monday.
Parent Child relationship
An attached child Task however is one which has a special relationship with the parent:
- The parent waits for the child to finish before it completes
- If the child Task throws an exception then the parent will catch it and re-throw it
- The status of the parent depends on the status of the child
Task parentTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Starting child task...");
Task childTask = Task.Factory.StartNew(() =>
{
Console.WriteLine("Child running. Going to sleep for a sec.");
Thread.Sleep(1000);
Console.WriteLine("Child finished and throws an exception.");
throw new Exception();
}, TaskCreationOptions.AttachedToParent);
});
try
{
Console.WriteLine("Waiting for parent task");
parentTask.Wait();
Console.WriteLine("Parent task finished");
}
catch (AggregateException ex)
{
Console.WriteLine("Exception: {0}", ex.InnerException.GetType());
}
Dealing with UI Thread and Task
Below code causes Cross Threading Exception
private void TestButton_Click(object sender, EventArgs e)
{
TestButton.Enabled = false;
var backgroundTask = new Task(() =>
{
Thread.Sleep(5000);
TestButton.Enabled = true; // Causes cross-threading exception
});
backgroundTask.Start();
}
Below code uses FromCurrentSynchronizationContext
to use Tasks for the GUI Thread
private void TestButton_Click(object sender, EventArgs e)
{
TestButton.Enabled = false;
var uiThreadScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var backgroundTask = new Task(() =>
{
Thread.Sleep(5000);
});
var uiTask = backgroundTask.ContinueWith(t =>
{
TestButton.Enabled = true;
}, uiThreadScheduler);
backgroundTask.Start();
}
References
TaskCreationOptions Enum
How to: Chain Multiple Tasks with Continuations
Creating an attached child Task in .NET C#
TaskContinuationOptions Enum
Attaching Continuation Tasks to the UI Thread