DEV Community

Cover image for C# Asynchronous Programming Full Tutorial : Async / Await / Task and Deadlock Fix by ConfigureAwait
Furkan Gözükara
Furkan Gözükara

Posted on

C# Asynchronous Programming Full Tutorial : Async / Await / Task and Deadlock Fix by ConfigureAwait

Here the full video regarding the lecture:

https://www.youtube.com/watch?v=I4cnX_odC1M

Image description

Asynchronous programming is a key aspect of modern software development, especially when working with the .NET framework. It allows your application to perform multiple tasks concurrently, improving performance and responsiveness. In this article, we'll look at how to do asynchronous programming with C# and .NET, including how to use some of the key features and tools for managing asynchronous code.

To begin with, let's define what we mean by asynchronous programming. In simple terms, asynchronous programming is a way of writing code that can execute multiple tasks concurrently, rather than sequentially. This means that instead of executing one task at a time, your code can start multiple tasks at once and then "wait" for them to finish before continuing. This is particularly useful in scenarios where you need to perform a long-running or resource-intensive operation, such as making an HTTP request or accessing a database.

One of the key features of asynchronous programming in C# and .NET is the async and await keywords. These keywords allow you to easily write asynchronous code that is still easy to read and understand. Here's a simple example of how to use these keywords to make an asynchronous HTTP request:

using System.Net.Http;

async Task<string> GetContentAsync(string url)
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(url);
        return await response.Content.ReadAsStringAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

In this code, we're using the async keyword to declare that the GetContentAsync method is asynchronous. We're also using the await keyword inside the method to asynchronously wait for the GetAsync and ReadAsStringAsync methods to complete. This allows us to write the code in a way that looks like it's executing sequentially, but it's actually running asynchronously under the hood.

Another important concept in asynchronous programming with .NET is the Task class. This class represents an asynchronous operation, and it allows you to manage and control that operation. For example, you can use the Task.Wait method to block the current thread and wait for a task to complete. Here's an example:

var contentTask = GetContentAsync("https://example.com");

// Wait for the task to complete before continuing.
contentTask.Wait();

// Get the result of the task.
var content = contentTask.Result;
Enter fullscreen mode Exit fullscreen mode

In this code, we're using the Wait method to block the current thread and wait for the GetContentAsync task to complete. We're then using the Result property to get the result of the task. This is useful in scenarios where you need to ensure that a task has completed before moving on to the next step in your code.

Another way to manage tasks in asynchronous programming is to use the ContinueWith method. This method allows you to specify a continuation action that will be executed when a task completes. Here's an example:

var contentTask = GetContentAsync("https://example.com");

// Specify a continuation action that will be executed when the task completes.
contentTask.ContinueWith(t =>
{
    // Get the result of the task.
    var content = t.Result;

    // Do something with the content.
});
Enter fullscreen mode Exit fullscreen mode

Another important aspect of asynchronous programming with C# and .NET is the ConfigureAwait method. This method allows you to control how the await keyword behaves in your code. By default, the await keyword will automatically capture the current context (such as the current thread or synchronization context) and resume execution on that same context when the awaited task completes. This is usually the desired behavior, but there are some cases where you might want to avoid capturing the context.

One example is when you're writing code that will be executed on a thread that has a limited lifetime, such as a UI thread in a Windows Forms or WPF application. In this case, you can use the ConfigureAwait method to prevent the await keyword from capturing the current context and instead resume execution on a different context. Here's an example:

// Prevent the await keyword from capturing the current context.
var content = await GetContentAsync("https://example.com").ConfigureAwait(false);
Enter fullscreen mode Exit fullscreen mode

In this code, we're using the ConfigureAwait method to prevent the await keyword from capturing the current context. This means that when the GetContentAsync task completes, the code will resume execution on a different context, rather than the current UI thread. This can improve performance and avoid potential deadlock situations.

In conclusion, asynchronous programming is a powerful and essential aspect of modern software development with C# and .NET. It allows you to write code that can perform multiple tasks concurrently, improving performance and responsiveness. The async and await keywords, along with the Task class and the ConfigureAwait method, provide a powerful set of tools for managing asynchronous code in your applications.

To update UI elements from different tasks, sub-tasks, and threads, one can use the Dispatcher.BeginInvoke method. This method enables one to execute code in the user interface (UI) thread asynchronously. This is especially useful when working with multiple threads, as each thread has its own UI thread.

To use Dispatcher.BeginInvoke, first, obtain an instance of the Dispatcher object associated with the UI thread that the code will run on. This can be done by calling the Dispatcher.CurrentDispatcher property. Next, call the BeginInvoke method on this Dispatcher instance, passing in a delegate that contains the code to be executed in the UI thread.

Here is an example of using Dispatcher.BeginInvoke to update a TextBox control with a string:

// Obtain the Dispatcher associated with the UI thread
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

// Create a delegate that contains the code to update the TextBox
Action updateTextBoxAction = () => textBox1.Text = "Hello World";
Enter fullscreen mode Exit fullscreen mode
// Asynchronously invoke the delegate in the UI thread
dispatcher.BeginInvoke(updateTextBoxAction);
Enter fullscreen mode Exit fullscreen mode

Another way to determine which code is executed in which thread is to use the Thread.CurrentThread.ManagedThreadId property. This property returns the unique identifier of the current managed thread. By checking this property at various points in the code, one can determine which thread the code is being executed on.

Here is an example of using Thread.CurrentThread.ManagedThreadId to output the thread ID of the current thread:

// Output the current thread ID
Console.WriteLine("Thread ID: {0}", Thread.CurrentThread.ManagedThreadId);
Enter fullscreen mode Exit fullscreen mode

To download the source of a web page asynchronously and properly using HttpClient and Content.ReadAsStringAsync, one can use the following code:

// Create a new instance of HttpClient
HttpClient client = new HttpClient();


// Download the source of the web page as a string asynchronously
string pageSource = await client.GetStringAsync("http://www.example.com");


// Output the source of the web page
Console.WriteLine(pageSource);
Enter fullscreen mode Exit fullscreen mode

In this code, the HttpClient.GetStringAsync method is used to download the source of the web page as a string asynchronously. The await keyword is used to wait for the asynchronous operation to complete before accessing the result. Finally, the source of the web page is output using the Console.WriteLine method.

It is important to note that using HttpClient as shown in this example is the recommended way to download web page source in .NET applications. The HttpClient class is designed for long-lived operations and uses a connection pool to improve performance and reduce network overhead. This is in contrast to the older WebClient class, which is not thread-safe and should not be used for multiple concurrent operations.

Top comments (0)