一、案例代码与反编译分析
1.1 原始示例代码
using System;
using System.Threading.Tasks;
private static async Task Main(string[] args)
{
Console.WriteLine(await Task.Factory.StartNew(() => Task.FromResult(0)));
}
1.2 反编译核心结构
使用ILSpy反编译后代码, 切换到
C# 4.0 / VS 2010
以查看生成的状态机
1.2.1 Main
函数
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
[AsyncStateMachine(typeof(<Main>d__0))]
[DebuggerStepThrough]
private static Task Main(string[] args)
{
<Main>d__0 stateMachine = new <Main>d__0();
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.args = args;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
1.2.2 状态机代码
仅显示核心函数
MoveNext
private sealed class <Main>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public string[] args;
private Task<int> <res>5__1;
private Task<int> <>s__2;
private TaskAwaiter<Task<int>> <>u__1;
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<Task<int>> awaiter;
if (num != 0)
{
awaiter = Task.Factory.StartNew(() => Task.FromResult(0)).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<Main>d__0 stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<Task<int>>);
num = (<>1__state = -1);
}
<>s__2 = awaiter.GetResult();
<res>5__1 = <>s__2;
<>s__2 = null;
Console.WriteLine(<res>5__1);
}
catch (Exception exception)
{
<>1__state = -2;
<res>5__1 = null;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<res>5__1 = null;
<>t__builder.SetResult();
}
...
}
二、异步状态机工作机制
2.1 初始化流程
- 创建状态机实例
生成继承IAsyncStateMachine
的私有类实例,保存上下文信息 - 构建异步方法组件
AsyncTaskMethodBuilder.Create()
创建异步操作的核心控制器 - 设置初始状态
通过<>1__state = -1
标记为初始执行状态 - 启动状态机
调用构建器的Start()
方法触发首次MoveNext
执行
2.2 执行阶段分析
2.2.1 第一次调用MoveNext
通过执行
AsyncTaskMethodBuilder
的Start
方法,进行状态机MoveNext
的第一次调用。
2.2.1.1 此次调用MoveNext
会产生两种结果:
- Task同步完成,直接返回Task的Result。
- 一般会出现在类似
Task t = Task.FromResult(0)
这种情况,创建完Task, 能够立即完成的。 - 示例代码使用的是
Task.Factory.StartNew()
,不会出现这种情况。
- 一般会出现在类似
- Task未同步完成,进入等待状态。
2.2.1.2 源码
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<Task<int>> awaiter;
// 当前num==-1, 进入到此if分支
if (num != 0)
{
// 获取当前Task的Awaiter=>TaskAwaiter
awaiter = Task.Factory.StartNew(() => Task.FromResult(0)).GetAwaiter();
// 当前Task未直接完成
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<Main>d__0 stateMachine = this;
// 进入等待状态
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
}
// 不会进入此分支
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<Task<int>>);
num = (<>1__state = -1);
}
// 如果当前任务同步完成,直接获取当前Task结果
<>s__2 = awaiter.GetResult();
<res>5__1 = <>s__2;
<>s__2 = null;
Console.WriteLine(<res>5__1);
}
catch (Exception exception)
{
<>1__state = -2;
<res>5__1 = null;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<res>5__1 = null;
<>t__builder.SetResult();
}
2.2.2 准备进入等待状态
通过调用
<>t__builder.AwaitUnsafeOnCompleted
进入等待状态。
2.2.2.1 源码
internal static void AwaitUnsafeOnCompleted<TAwaiter>(
ref TAwaiter awaiter, IAsyncStateMachineBox box)
where TAwaiter : ICriticalNotifyCompletion
{
// 传入awaiter为TaskAwaiter, 进入此if分支
if ((null != (object?)default(TAwaiter)) && (awaiter is ITaskAwaiter))
{
ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); // relies on TaskAwaiter/TaskAwaiter<T> having the same layout
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true);
}
...
}
2.2.3 追加MoveNext
方法到Task
延续任务。
通过调用
TaskAwaiter.UnsafeOnCompletedInternal
将异步状态机中的MoveNext
追加到Task的延续对象中
2.2.3.1 源码
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
// Create the best AwaitTaskContinuation object given the request.
// If this remains null by the end of the function, we can use the
// continuationAction directly without wrapping it.
TaskContinuation? tc;
if (continueOnCapturedContext)
{
if (SynchronizationContext.Current is SynchronizationContext syncCtx && syncCtx.GetType() != typeof(SynchronizationContext))
{
tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, stateMachineBox.MoveNextAction, flowExecutionContext: false);
goto HaveTaskContinuation;
}
if (TaskScheduler.InternalCurrent is TaskScheduler scheduler && scheduler != TaskScheduler.Default)
{
tc = new TaskSchedulerAwaitTaskContinuation(scheduler, stateMachineBox.MoveNextAction, flowExecutionContext: false);
goto HaveTaskContinuation;
}
}
// Otherwise, add the state machine box directly as the continuation.
// If we're unable to because the task has already completed, queue it.
if (!AddTaskContinuation(stateMachineBox, addBeforeOthers: false))
{
ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, preferLocal: true);
}
return;
HaveTaskContinuation:
if (!AddTaskContinuation(tc, addBeforeOthers: false))
{
tc.Run(this, canInlineContinuationTask: false);
}
}
2.2.4 完成Task,执行延续任务。
在Task执行完成后,会调用
FinishContinuations
执行其延续任务
2.2.4.1 执行延续任务时,调用异步状态机中的MoveNext
方法,恢复代码执行。
2.2.4.2 源码
仅列出延续任务仅是单个的情况,多个(不仅包含异步状态机的MoveNext)的类似
private void RunContinuations(object continuationObject) // separated out of FinishContinuations to enable it to be inlined
{
bool canInlineContinuations =
(m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) == 0 &&
RuntimeHelpers.TryEnsureSufficientExecutionStack();
switch (continuationObject)
{
// Handle the single IAsyncStateMachineBox case. This could be handled as part of the ITaskCompletionAction
// but we want to ensure that inlining is properly handled in the face of schedulers, so its behavior
// needs to be customized ala raw Actions. This is also the most important case, as it represents the
// most common form of continuation, so we check it first.
case IAsyncStateMachineBox stateMachineBox:
AwaitTaskContinuation.RunOrScheduleAction(stateMachineBox, canInlineContinuations);
LogFinishCompletionNotification();
return;
// Handle the single Action case.
case Action action:
AwaitTaskContinuation.RunOrScheduleAction(action, canInlineContinuations);
LogFinishCompletionNotification();
return;
// Handle the single TaskContinuation case.
case TaskContinuation tc:
tc.Run(this, canInlineContinuations);
LogFinishCompletionNotification();
return;
// Handle the single ITaskCompletionAction case.
case ITaskCompletionAction completionAction:
RunOrQueueCompletionAction(completionAction, canInlineContinuations);
LogFinishCompletionNotification();
return;
}
// Not a single; it must be a list.
List<object?> continuations = (List<object?>)continuationObject;
//
// Begin processing of continuation list
//
...
}
2.2.5 MoveNext
第二次
第二次调用
MoveNext
方法后,会恢复后续代码的执行。
2.2.5.1 源码
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<Task<int>> awaiter;
if (num != 0)
{
awaiter = Task.Factory.StartNew(() => Task.FromResult(0)).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<Main>d__0 stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
}
// 此时num==0, 进入此分支
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<Task<int>>);
num = (<>1__state = -1);
}
// 获取执行结果
<>s__2 = awaiter.GetResult();
<res>5__1 = <>s__2;
<>s__2 = null;
// 执行await后续的代码
Console.WriteLine(<res>5__1);
}
catch (Exception exception)
{
<>1__state = -2;
<res>5__1 = null;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<res>5__1 = null;
<>t__builder.SetResult();
}
三、同步上下文捕获
3.1 默认情况
在执行阶段2.2.2 准备进入等待状态时, 会正常捕获当前同步上下文。
3.1.1 源码
// struct: AsyncTaskMethodBuilder<TResult>
internal static void AwaitUnsafeOnCompleted<TAwaiter>(
ref TAwaiter awaiter, IAsyncStateMachineBox box)
where TAwaiter : ICriticalNotifyCompletion
{
// The null tests here ensure that the jit can optimize away the interface
// tests when TAwaiter is a ref type.
if ((null != (object?)default(TAwaiter)) && (awaiter is ITaskAwaiter))
{
ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); // relies on TaskAwaiter/TaskAwaiter<T> having the same layout
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true);
}
}
// class: Task
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
// Create the best AwaitTaskContinuation object given the request.
// If this remains null by the end of the function, we can use the
// continuationAction directly without wrapping it.
TaskContinuation? tc;
// If the caller wants to continue on the current context/scheduler and there is one,
// fall back to using the state machine's delegate.
if (continueOnCapturedContext)
{
if (SynchronizationContext.Current is SynchronizationContext syncCtx && syncCtx.GetType() != typeof(SynchronizationContext))
{
tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, stateMachineBox.MoveNextAction, flowExecutionContext: false);
goto HaveTaskContinuation;
}
if (TaskScheduler.InternalCurrent is TaskScheduler scheduler && scheduler != TaskScheduler.Default)
{
tc = new TaskSchedulerAwaitTaskContinuation(scheduler, stateMachineBox.MoveNextAction, flowExecutionContext: false);
goto HaveTaskContinuation;
}
}
}
3.2 添加ConfigureAwait(false)
在
await Taskxxx()
后追加.ConfigureAwait(false)
, 即await Taskxxx().ConfigureAwait(false)
。则会生成ConfiguredTaskAwaiter
, 阻止同步上下文切换。
3.2.2 源码
// struct: AsyncTaskMethodBuilder<TResult>
internal static void AwaitUnsafeOnCompleted<TAwaiter>(
ref TAwaiter awaiter, IAsyncStateMachineBox box)
where TAwaiter : ICriticalNotifyCompletion
{
else if ((null != (object?)default(TAwaiter)) && (awaiter is IConfiguredTaskAwaiter))
{
ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, (ta.m_options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0);
}
}
// class: Task
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
// Otherwise, add the state machine box directly as the continuation.
// If we're unable to because the task has already completed, queue it.
if (!AddTaskContinuation(stateMachineBox, addBeforeOthers: false))
{
ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, preferLocal: true);
}
return;
}
四、同步等待Task结果
4.1 Task.Result
vs Task.GetAwaiter().GetResult()
4.1.1 Task.Result
- 如果任务未完成,会同步阻塞当前线程直到任务完成。
- 返回任务的结果(若任务是 Task)。
- 异常处理:如果任务抛出异常,异常会被包装成
AggregateException
抛出。
4.1.2 Task.GetAwaiter().GetResult()
- 如果任务未完成,也会同步阻塞当前线程直到任务完成。
- 返回任务的结果(若任务是 Task)。
- 异常处理:直接抛出任务原始的异常(而非
AggregateException
)。
4.1.3 总结
- 在未明确需要处理
AggregateException
异常的情况下,优先选用Task.GetAwaiter().GetResult()
。 - 两者均会造成当前线程阻塞,所以优先使用
await
来等待Task完成并获取执行结果。