Skip to Content
Course content

158: Common C# Interview Questions on Async/Await

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've sat through dozens of technical interviews, and there is one specific trap that catches almost every junior-to-mid-level C# developer: the belief that adding the async keyword to a method magically makes it run on a background thread.

Async is Not a Magic "Run on Another Thread" Button

If you tell an interviewer that async enables multi-threading, they'll know immediately that you've memorized syntax but haven't wrestled with the runtime. Here is the concrete proof. Look at this code:

public async Task DoWorkAsync()
{
    // I've marked this async, so it must be multi-threaded, right?
    Console.WriteLine("Starting work...");
    Thread.Sleep(2000); // Blocking call!
    Console.WriteLine("Work finished.");
}

If you call await DoWorkAsync() from a UI thread, your application will freeze for two seconds. Why? Because the async keyword doesn't actually start a new thread; it just tells the compiler, "This method is allowed to use the await keyword and should be transformed into a state machine." Until the code hits an await on a task that is actually incomplete, everything runs synchronously on the calling thread.

To actually offload work, you need Task.Run or a truly asynchronous I/O operation (like HttpClient.GetAsync). I always tell my mentees: async is about yielding, not parallelism. It's the difference between a waiter standing at your table waiting for you to chew (synchronous) and a waiter taking your order and moving to another table until the food is ready (asynchronous).

The "Async Void" Red Flag

Another favorite interview question is: "When should you use async void?" The answer is almost always "Never," with one single exception: Event Handlers.

If you use async void in a regular method, you're creating a "fire-and-forget" scenario that is a nightmare to debug. Since there is no Task object returned, the calling code has no way to await the completion or catch exceptions. If an exception is thrown inside an async void method, it can't be caught by a try-catch block surrounding the call—it often crashes the entire process.

  • Task: Use this for methods that do work but return nothing.
  • Task<T>: Use this for methods that return a value.
  • void: Only for public async void Button_Click(object sender, EventArgs e).

Solving the Deadlock with ConfigureAwait(false)

In more senior interviews, you'll likely be asked about deadlocks in legacy ASP.NET or WinForms applications. This usually happens when someone mixes async code with blocking calls like .Result or .Wait().

Here's the scenario: The UI thread calls an async method and blocks on .Result. The async method finishes its work and tries to jump back onto the UI thread to finish the method (because that's the default behavior of the SynchronizationContext). But the UI thread is still blocking, waiting for the result. They are both waiting for each other. Deadlock.

The fix is ConfigureAwait(false). By adding this to your awaits, you're telling the runtime: "I don't need to return to the original context (the UI thread) to finish this work."

public async Task<string> FetchDataAsync()
{
    var result = await _client.GetStringAsync("https://api.example.com")
                              .ConfigureAwait(false); 
    return result.ToUpper(); // This now runs on a thread pool thread, not the UI thread.
}

I'll be honest: in modern .NET (Core/5/6/7+), this is less of an issue because ASP.NET Core doesn't have a SynchronizationContext. But in a library meant to be used by anyone, ConfigureAwait(false) is still a best practice.




📋 Practical Task

Fixing the UI-Blocking "Fake Async" Method

You've been handed a piece of code from a teammate. They claim they've made the data processing asynchronous to keep the UI responsive, but the application is still freezing during the "Heavy Processing" phase.

Your Task: Modify the ProcessDataAsync method so that the heavy CPU-bound work actually runs on a background thread, ensuring the UI remains responsive. Also, ensure the method signature is correct for an asynchronous operation that doesn't return a value.

public class DataService
{
    // This method is currently blocking the UI thread
    public async void ProcessDataAsync() 
    {
        Console.WriteLine("Processing started...");
        
        // Simulate heavy CPU-bound work (e.g., complex calculations)
        // This is the part causing the UI freeze
        Thread.Sleep(5000); 
        
        Console.WriteLine("Processing complete!");
    }
}

Requirements:

  • Change the return type to avoid "fire-and-forget" pitfalls.
  • Wrap the blocking call in the appropriate mechanism to offload it to the Thread Pool.
  • Ensure the method can be properly awaited by the caller.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.