Interview Question?-?Why Use Async Functions in Sequential Tasks?

Interview Question?-?Why Use Async Functions in Sequential Tasks?

I Failed to Answer This Question in an Interview Before!

The answer lies in understanding the differences between asynchronous programming, concurrency, and parallelism. Let me walk you through my journey of learning this, starting with an interview question that left me thinking.

The Interview That Confused Me:

Interviewer: Why do we use async functions in C#? Me: We use them so that functions can work simultaneously without the need to wait for each other.

Interviewer: Imagine you have an async function that calls three other async functions inside it, using the await keyword. How does this function work? Me: Each method will execute sequentially because of the await keyword. The first function runs, then the second starts after the first completes, and the third follows after the second.

Interviewer: Okay, but if the methods are executed sequentially, why do we need async? Couldn’t we just make this synchronous?

At that moment, I realized I didn’t fully understand the role of async. The interviewer’s question was spot-on: if tasks execute sequentially, why even bother with async? To answer this, I had to dig deeper into the differences between asynchronous programming, concurrency, and parallelism.


Understanding Some Cornerstone Concepts:

The visual below illustrates the difference between concurrency and parallelism.


Image by LevelUpCoding

Let’s first clarify these concepts:

  • Concurrency: is a way to structure a program to manage multiple tasks at the same time, potentially overlapping in their execution.
  • Parallelism: is about using multiple threads or processors to execute multiple tasks simultaneously.
  • Asynchronous programming: is a tool for achieving non-blocking execution, allowing tasks to pause and resume without occupying system resources unnecessarily.


Why Use Async in Sequential Tasks?

So returning to the main question, the primary reason to use async in sequential tasks is to ensure non-blocking execution. Even though the tasks execute one after the other, using await allows the thread to remain free while waiting for long-running operations, such as I/O or network requests. This improves resource utilization and ensures that other parts of the program, like the UI or event handlers, remain responsive.

What Happens Behind the Scenes? (For the Curious)

When an async function with await is executed:

  1. The async keyword enables the function to run asynchronously, meaning it can yield control back to the caller when it encounters an await.
  2. The await keyword pauses the function at that point, allowing the runtime to handle other tasks while waiting for the awaited operation to be completed.
  3. Once the awaited task is completed, the function resumes execution from where it left off.

This approach ensures the application remains responsive and makes efficient use of threads, even when the operations are sequential.


A Simple Code Example

We define three simple tasks to simulate real-world operations like washing dishes, vacuuming, and doing laundry. Each task takes 1 second to complete.

Example 1: Running Sequentially

static async Task PerformTasksAsync()
{
    await WashDishesAsync();
    await VacuumLivingRoomAsync();
    await DoLaundryAsync();
}

static async Task WashDishesAsync() => await Task.Delay(1000);
static async Task VacuumLivingRoomAsync() => await Task.Delay(1000);
static async Task DoLaundryAsync() => await Task.Delay(1000);        

What Happens? Each task is executed sequentially, but the thread remains free during the await, allowing for non-blocking execution.


Example 2: Running Concurrently

To run tasks concurrently, start them all at once and wait for their completion:

static async Task PerformTasksConcurrentlyAsync()
{
    var task1 = WashDishesAsync();
    var task2 = VacuumLivingRoomAsync();
    var task3 = DoLaundryAsync();
    await Task.WhenAll(task1, task2, task3);
}        

What Happens?

Tasks start simultaneously, and the program waits for all to finish together.


Example 3: Running in Parallel

For true parallelism, tasks must run on separate threads:

static async Task PerformTasksInParallelAsync()
{
    var tasks = new List<Func<Task>>
    {
        WashDishesAsync,
        VacuumLivingRoomAsync,
        DoLaundryAsync
    };

    await Parallel.ForEachAsync(tasks, async (task, _) => await task());
}        

What Happens?

Tasks are executed on multiple threads or processors, leveraging hardware for better performance.


Final Thoughts:

The goal of asynchronous programming isn’t to guarantee parallelism or concurrency but rather to achieve non-blocking execution. Even in sequential tasks, async allows threads to remain free for other operations, making it a powerful tool for efficient programming.

Nermin Kaya ??

Software Engineer | Backend | C# | .NET

2 个月

LinkedIn, please address the bug with GIF images and fix the issue where tags are not functioning properly in image captions. ??

要查看或添加评论,请登录

社区洞察

其他会员也浏览了