Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested async method blocks without await

I'm not sure about what's going on here. As I understand, as soon as a method encounters the await keyword, it effectively returns, and later continues executing once the Task is completed. However, that is not the behavior that I am observing. I have two simple methods:

public static async Task<bool> Level1()
{            
    var t = await Level2();      
    Console.WriteLine("L1 done.");  
    return t;                     
}

public static async Task<bool> Level2()
{
    int delay = 10;
    Thread.Sleep(100);
    var t = new TaskFactory();

    t.StartNew(() => { Thread.Sleep(delay); });
    Console.WriteLine("L2 done.");
    return true;
}

And in main, I call

static void Main(string[] args)
{
    Level1();
    Console.WriteLine("Main done.");
}

Now, I would expect Main() to continue executing after the call to Level1(), since it is not awaited, and because Level1() contains an await keyword. The resulting output, however, is

L2 done.
L1 done.
Main done.

Indicating that my async methods are running synchronously. Modifying Level2() to use the await keyword solves the problem, and the Level1() call no longer blocks. Why do I need to use "await" in the nested method Level2() method to keep Level1() from blocking Main?

like image 599
user2647513 Avatar asked Apr 15 '26 03:04

user2647513


1 Answers

Marking a method with the async keyword doesn't make it asynchronous. It only allows to use await (and wraps the result, or exceptions with a task).

If you have an "async" method that doesn't await any asynchronous operation inside it the method is completely synchronous and will run on the calling thread.

What you're probably missing is that using await doesn't necessarily makes the method run asynchronously as well, using await on an uncompleted task does. When the task is already completed there's no reason to suspend the method. You can just get the result out of the task and keep running synchronously.

In your case since Level2 runs synchronously. When Level1 is ready to await its task it's already completed so Level2 keeps running synchronously. Keep in mind that you await a task, and not a method so your code actually runs like this:

public static async Task<bool> Level1()
{            
    var $task = Level2(); // Runs synchronously
    var t = await $task; // Awaiting a completed task      
    Console.WriteLine("L1 done.");  
    return t;                     
}
like image 189
i3arnon Avatar answered Apr 17 '26 04:04

i3arnon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!