3

I'm trying to understand async/await and have read a number of articles but am still confused about the synchronous/asynchronous nature.

I have the following test console app:

static void Main(string[] args)
{
    var test = FooAsync();
    Console.WriteLine("After FooAsync");

    for (int i = 0; i < 100; i++)
        Console.WriteLine("After that");

    Console.ReadKey();
}

private static async Task FooAsync()
{
    Console.WriteLine("Before delay");
    await Task.Delay(1);
    Console.WriteLine("After delay");
}

The code gives output along the lines of:

Before delay
After FooAsync
After that
After that
After that
After that
After delay
After that
.
.

I understand that async/await will not create a separate thread for processing and that at the point FooAsync reaches the await Task.Delay(1) line it will return back to Main as the task will not yet have completed, however, as we are only running on a single thread can someone explain what triggers the FooAsync method to resume at some arbitrary point within Main before Main can then continue?

Update I take it back and i3arnon and dariogriffo are correct. The code does use multiple threads (as I'd have seen before had looked in the debugger or done the obvious as kha suggested). I'd been confused by the Threads section on the following page https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_Threads not realising that a "continuation" actually refers to a continuation task schedule to run as soon as the task being "awaited" finishes.

IAmConfused
  • 123
  • 2
  • 10
  • 1
    Answer by i3arnon is correct but if you want to understand it fully, replace your FooAsync with this: `private static async Task FooAsync() { Console.WriteLine("Before delay on " + Thread.CurrentThread.ManagedThreadId); await Task.Delay(1); Console.WriteLine("After delay on " + Thread.CurrentThread.ManagedThreadId); }` and see that before and after are done on separate threads. – kha May 29 '15 at 10:42

3 Answers3

5

This isn't single threaded.

When the delay task completes the rest of the method is posted to the ThreadPool and runs concurrently with your main thread. The "trigger" here is the callback of the internal System.Threading.Timer being used inside Task.Delay.

This behaviour depends on the SynchronizationContext. In a UI environment this would have been posted to the same UI thread and would have to wait until that thread is free.

If you would have been waiting for the task returned from FooAsync then you would only have a single thread running each time.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • See [this answer](http://stackoverflow.com/a/9212343/1497596) for how to wait for the task returned by `FooAsync` within `Main` so that the continuation for `FooAsync` runs on the main thread. Also, note that without waiting for `FooAsync`, an exception thrown by `FooAsync` will be silently swallowed. – DavidRR May 23 '16 at 15:03
2

Async/await may create new threads OR NOT, it depends of the nature of the operation. If the operation is an IO (for example disks/network operations) probably is coded in a way it will not spin a new thread. You can read from here:

The async and await keywords don't cause additional threads to be created?

If you create your own Async operation and you create a thread, that's a different story, that's why you shouldn't do async over sync

http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

You can check this also but using Thread.CurrentThread to get the Id of the process. (Add that to a Console.WriteLine)

Community
  • 1
  • 1
dariogriffo
  • 4,148
  • 3
  • 17
  • 34
0

It's a pretty common misconception that the async or await keywords create new threads. They don't.

The threads are created by running a Task. In this case, the thread is created by the Task.Delay call.

craftworkgames
  • 9,437
  • 4
  • 41
  • 52