The Task and Task<TResult> types are already in the .NET Framework 4. A Task represents an ongoing activity, which may be CPU intensive work running on a separate thread, but may also represent an I/O operation, for example an outstanding response for an internet request. Task represents an activity without a result, and Task<TResult>, which derives from Task, represents an activity with a result of type TResult.
Tasks are often used to represent CPU-bound work running on a separate thread. Saying
Task<double> task = Task.Run(() => LongRunningComputation(x, y, z)); Dim task As Task(Of Double) = Task.Run(Function() LongRunningComputation(x, y, z))
is an easy way to schedule the work in the lambda on a thread pool thread, immediately returning a task that will complete when the work is done.
Manually creating a Task that does not run on a separate thread is also easy:
var tcs = new TaskCompletionSource<double>(); return tcs.Task; … tcs.TrySetResult(result); Dim tcs = New TaskCompletionSource(Of Double)() Return tcs.Task … tcs.TrySetResult(result)
Once you have a TaskCompletionSource you can immediately hand out its associated Task, and complete it later when the work is done and the result is ready. The tasks generated by the async language feature are of the latter sort – they don’t occupy a thread of their own.
Tasks can represent exceptional as well as successful completion. If a Task ends up in a faulted state, the relevant Exception will be available, and awaiting the Task will result in its exception being propagated.
The easiest way to consume a Task is to await it in an async method, but there are also plenty of more “manual” ways to consume it – synchronously or asynchronously.
The Task-based asynchronous pattern
Part of this proposal is a shift to a new model for what asynchronous methods should look like – the Task-based Asynchronous Pattern (TAP). Current patterns offer one method for launching the operation and another method or event for obtaining the result. By returning a Task or Task<T> instead, as in the example above, only one method is needed, and all further interaction with the future result is done through the Task.