The primary thread that keeps your app alive is effectively:
private static void TheRealEntryPoint() => Main().GetAwaiter().GetResult();
(which is broadly the same as .Wait()
, and is the kind of "sync-over-async" thing that you should never write in your own code, but ... which suffices in this specific scenario)
As such:
- the
Task.Run
returns an incomplete task
- thus the
await
on that returns to the caller, taking us back to Main()
- in an incomplete state, so that
await
also returns to the caller - so we end up in TheRealEntryPoint
- where the primary thread simply blocks
- ...
- at some point, the thread-pool picks up the work item and runs it
- marking the task as complete
- which reactivates
F1
, which can now mark itself as complete
- which reactivates
Main
, which can now mark itself as complete
- which unblocks the primary thread that is stuck in
TheRealEntryPoint
- which allows the exe to terminate