Explanation
What just happened
Main started on thread 1 and called FetchAsync, which ran on that same thread up to its await. Task.Delay needs no thread while it waits: FetchAsync returned to its caller, and a timer resumed it later. The rest of FetchAsync, and then Main, ran on a thread-pool thread, not thread 1.
Try the what-if switch
- Task.Run:
Crunchis pure CPU work, soTask.Runputs it on a pool thread.Mainawaits it and continues on the thread that ran it.
Why
await splits a method in two: the part before runs now, the part after is scheduled when the awaited task finishes. Where that second part runs depends on the SynchronizationContext. A console app has none, so it runs on the pool.
Watch out
Don’t assume you’re on the same thread after await. Anything tied to the thread, like [ThreadStatic] fields or ThreadLocal<T> values, can be different on the other side.
Common questions
Does await create a new thread in C#?
No. The code before the first await runs on the caller's thread. While the awaited work is pending, the method has returned to its caller; when it completes, the rest runs on whichever thread the context picks.
Which thread runs the code after await?
In a console app, a thread-pool thread: there's no SynchronizationContext to return to. In a UI app, the context posts it back to the UI thread.
When should I use Task.Run?
For CPU-bound work you want off the current thread. Task.Run queues it on the thread pool; awaiting it frees the caller until it's done.
Verified: this timeline matches a real program, checked in CI on .NET 8 and 10. How we verify. Unclear or wrong? Tell us