0

I'm trying to understand async await behavior and in particular how it affects the main thread, so my question is related to below code:

    static async Task main(string[] args)
    {
        await LongAction();
    }

    static Task LongAction() => Task.Delay(10000);

what will happen with the main thread when LongAction is being executed. As I understand, if the the caller thread is not the main (let's call it thread2), during the time when awaitable operation is in play, this thread (thread2) will be returned to the ThreadPool (with default task scheduler). But can it happen with the main thread (it doesn't looks so, since initially it wasn't taken from TP)? What if the main thread is UI thread with thread context?

Rand Random
  • 7,300
  • 10
  • 40
  • 88
Unnamed
  • 111
  • 8
  • A UI-thread has always a mainloop. But on your example. By supporting async `main()` method, the caller must have a loop, and loops until the Task _(returned by the main)_ is marked as completed. In the example, the method is marked as async, so it will turn into a statemachine. When the last state is completed, it terminates the loop. – Jeroen van Langen May 03 '23 at 22:19
  • sorry what `loop` do you mean? – Unnamed May 03 '23 at 22:22
  • Some initial code calles your main method. – Jeroen van Langen May 03 '23 at 22:24
  • sorry still didn't get it, are you talking about code generated by the compiler (state machine)? – Unnamed May 03 '23 at 22:29
  • 1
    Use await Task.Delay when you want a logical delay without blocking the current thread. .this is site usefull to understand async-and-await: https://code-maze.com/asynchronous-programming-with-async-and-await-in-asp-net-core/ – abolfazl sadeghi May 03 '23 at 22:34
  • Probably reading https://stackoverflow.com/questions/9208921/cant-specify-the-async-modifier-on-the-main-method-of-a-console-app/9212343#9212343 and https://learn.microsoft.com/en-us/archive/blogs/mazhou/c-7-series-part-2-async-main should give you enough information... Maybe even duplicate (not really interested in reading it completely to confirm) – Alexei Levenkov May 03 '23 at 22:36

1 Answers1

4

in particular how it affects the main thread

async and await has no special behavior with any particular kind of thread, including main threads. It behaves the same way regardless of thread. Different frameworks, however, do treat main threads specially.

As I understand, if the the caller thread is not the main (let's call it thread2), during the time when awaitable operation is in play, this thread (thread2) will be returned to the ThreadPool (with default task scheduler).

The behavior of await is always the same: if the awaitable (i.e., the task) is not yet completed, then it returns. It's up to the framework what it does next with that thread. In the case of a thread pool thread, then yes, that thread is returned to the thread pool.

what will happen with the main thread when LongAction is being executed.

The .NET runtime includes a stub that invokes a Task-returning Main method and then just blocks that thread until the task completes. This is necessary because in console applications, if the main thread exits, the application exits.

What if the main thread is UI thread with thread context?

All UI frameworks have a "message loop" that processes messages about user input and other events. In that case, the thread just returns to its main loop and continues processing other events.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • thanks a lot for your answer! I'm still not confident about this: `It's up to the framework what it does next with that thread. In the case of a thread pool thread, then yes, that thread is returned to the thread pool.`. - what if the initial/main thread is not taken from the ThreadPool (I assume when you create a console application, the main thread is created via something close to `new Thread`). This main thread just sleeps and can't be reused? – Unnamed May 03 '23 at 22:51
  • 1
    @Unnamed: If you have an `async Main`, then yes, the thread just blocks and can't be reused. – Stephen Cleary May 03 '23 at 22:52
  • sorry, one more probably stupid question regarding above. If in the above case, the main thread is just blocked and let's say we know that logic inside LongAction operation (let's say it's IO bound) has no nested task calls. Is there any benefit in async workflow here comparing it with a sync call? – Unnamed May 03 '23 at 23:07
  • just to highlight message, cc: @Stephen Cleary – Unnamed May 03 '23 at 23:18
  • 1
    The entry point of a program, is *not* `Main`, but a function supplied by the dotnet runtime. It's the runtime's job to initialise any features of the language that are not provided by the operating system, then call your `Main` method. In the case of `async Task Main`, the runtime function will block until the task is complete. If the task is already complete, then there's nothing to wait for. BTW this is also true of programs written in other languages like C or win32 programs with a `WinMain` method, where the compiler provides a runtime entry point. – Jeremy Lakeman May 04 '23 at 00:46
  • 1
    `Is there any benefit in async workflow here comparing it with a sync call?` It's async; this is useful if you have an async-only API or if you're prototyping code that is expected to be async in its final form. It also allows asynchronous concurrency (e.g., `Task.WhenAll`). If you only have one method, then there's no threading benefits of async in this case. – Stephen Cleary May 04 '23 at 00:58
  • 1
    "The .NET runtime includes a stub that invokes a Task-returning Main method" AFAIK the C# compiler generates it, not the runtime. https://dotnetfiddle.net/y1jaRB – Charlieface May 04 '23 at 06:44