Abstract [ Documentation] [ Documentation]
Asynchronous programming allows for the execution of long-running operations that do not block the main thread.
Concepts:
- Synchronous operations complete sequentially; the next task does not start until the previous finishes.
- Asynchronous operations allow for starting a task then, while waiting for that task to finish, starting another.
- Parallel operations use multiple threads to execute several asynchronous tasks. They are deterministic: the order in which the tasks will complete is known.
- Concurrent operations use multiple threads to execute several asynchronous tasks. They are nondeterministic: the order in which the tasks will complete is not known.
.NET’s asynchronous programming pattern described here is an implementation of the promise model of asynchrony which uses the futures and promises constructs.
The asynchronous programming models uses Task
and Task<T>
objects which model asynchronous operations. Generally:
- For I/O-bound code,
await
aTask
orTask<T>
in a method markedasync
. - For CPU-bound code, await an operation that is started on a background thread via
Task.Run
.
An example of I/O-bound code: downloading data from a web service:
private readonly HttpClient _httpClient = new HttpClient();
downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await _httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
An example of CPU-bound code: performing a calculation in a game:
private DamageResult CalculateDamageDone()
{
// Code omitted: Performs an expensive calculation and returns the result of that calculation.
}
calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone() performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
Asynchronous vs. Synchronous
Asynchronous code like FryEggsAsync(2)
does not block:
// the async modifier signals that the method contains an await
static async Task Main()
{
Egg eggs = await FryEggsAsync(2);
Console.WriteLine("eggs are ready");
}
Synchronous code will block the thread executing it from doing any other work. It cannot be interrupted:
static void Main()
{
Egg eggs = FryEggs(2);
Console.WriteLine("eggs are ready");
}
Concurrent
Concurrent code like Task<Egg> eggsTask = FryEggsAsync(2)
does not block and allows for concurrent execution:
static async Task Main()
{
Task<Egg> eggsTask = FryEggsAsync(2);
Eggs eggs = await eggsTask;
Console.WriteLine("eggs are ready");
}
Composition
If any portion of an operation is asynchronous, the entire operation is asynchronous.
The composition of an asynchronous operation followed by synchronous work is asynchronous:
static async Task<Toast> MakeToastWithButterAndJamAsync()
{
var toast = await ToastBreadAsync(); // the asynchronous operation
ApplyButter(toast); // the synchronous operations
ApplyJam(toast);
return toast;
}
Async
Async Code & LINQ
Use caution. LINQ uses deferred (lazy) execution, so async calls won’t happen immediately (unless iteration is forced via .ToList()
or .ToArray()
)
Expression-Bodied Methods
If a method is expression-bodied, it can omit the async and await keywords:
static Task Main() => DoSomethingAsync();
…is equivalent to:
static async Task Main()
{
await DoSomethingAsync();
}
Other .NET Asynchronous Programming Patterns
These notes describe the Task Asynchronous Pattern, which is covered in more detail in the notes linked below.
Model | Abbr | Language constructs | Notes |
---|---|---|---|
Task Asynchronous Pattern Modern, easier to use | TAP | async /await | Notes on TAP |
Asynchronous Programming Model Legacy, more difficult to use | APM | IAsyncResult , Begin , End | Notes on APM Legacy pattern; not recommended for new development |
Event-based Asynchronous Pattern | EAP | Events, event-handler delegates, EventArg -derived types | Legacy pattern; not recommended for new development |
.NET also has a parallel programming pattern: the Task Parallel Library ( Notes on TPL).