Does .NET resume an await continuation on a new different thread pool thread or reuse the thread from a previous resumption?

后端 未结 4 1100
礼貌的吻别
礼貌的吻别 2021-01-20 23:30

Does .NET resume an await continuation on a new different thread pool thread or reuse the thread from a previous resumption?

Let\'s image below C# code in a .NET Core co

4条回答
  •  清歌不尽
    2021-01-21 00:03

    Does .NET resume an await continuation on a new different thread pool thread or reuse the thread from a previous resumption?

    Neither. By default, when awaiting Tasks, await will capture a "context" and use that to resume the asynchronous method. This "context" is SynchronizationContext.Current, unless it is null, in which case the context is TaskScheduler.Current. In your example code, the context is the thread pool context.

    The other part of the puzzle is undocumented: await uses the TaskContinuationOptions.ExecuteSynchronously flag. This means that when the Task.Run task is completed (by thread 4), its continuations are run immediately and synchronously - if possible. In your example code, the continuation may run synchronously because there's enough stack on thread 4 and the continuation should be run on a thread pool thread and thread 4 is a thread pool thread.

    Likewise, when AsyncThree completes, the continuation for AsyncTwo is run immediately and synchronously - again on thread 4 since it meets all the criteria.

    This is an optimization that is especially helpful in scenarios like ASP.NET, where it's common to have a chain of async methods and have one task completing (e.g., a db read) that completes the entire chain and sends the response. In those cases you want to avoid unnecessary thread switches.

    An interesting side effect of this is that you end up with an "inverted call stack" of sorts: the thread pool thread 4 ran your code and then completed AsyncThree and then AsyncTwo and then AsyncOne, and each of those completions are on the actual call stack. If you place a breakpoint on the WriteLine in AsyncOne (and look at external code), you can see where ThreadPoolWorkQueue.Dispatch (indirectly) called AsyncThree which (indirectly) called AsyncTwo which (indirectly) called AsyncOne.

提交回复
热议问题