- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
前一段时间得闲的时候优化了一下我之前的轮子 [DotNetCoreRpc] 小框架,其中主要的优化点主要是关于RPC异步契约调用的相关逻辑。在此过程中进一步了解了关于 async和await 异步操作相关的知识点,加深了异步操作的理解,因此总结一下。关于 async和await 每个人都有自己的理解,甚至关于 异步和同步 亦或者关于 异步和多线程 每个人也都有自己的理解。因此,如果本文涉及到个人观点与您的观点不一致的时候请勿喷。结论固然重要,但是在这个过程中的引发的思考也很重要.
大家应该都比较清楚 async和await 这对关键字是一组语法糖,关于语法糖大家可以理解为,编码过程中写了一个关键字,但是编译的时候会把它编译成别的东西,主要是用来提升开发效率。比如我有一段关于 async和await 相关的代码,如下所示 。
var taskOne = await TaskOne();
Console.WriteLine(taskOne);
Console.ReadLine();
static async Task<string> TaskOne()
{
var httpResponse = await ClassFactory.Client.GetAsync("https://www.cnblogs.com");
var content = await httpResponse.Content.ReadAsStringAsync();
return content;
}
public class ClassFactory
{
public static HttpClient Client = new HttpClient();
}
这段代码是基于c#顶级语句声明的,它是缺省Main方法的,不过在编译的时候编译器会帮我们补齐Main方法,因为执行的时候 JIT 需要Main方法作为执行入口。关于如何查看编译后的代码。我经常使用的是两个工具,分别是 ILSpy 和 dnSpy 。这俩工具的区别在于 ILSpy 生成的代码更清晰, dnSpy 生成的源码是可以直接调试的。需要注意的是如果使用的是 ILSpy 如果查看语法糖本质的话,需要在 ILSpy 上选择比语法糖版本低的版本,比如c# async和await关键字是在c# 5.0版本中引入的,所以我们这里我们在 ILSpy 里需要选择c#4.0或以下版本,入下图所示 如果使用的是 dnSpy 的话,需要在 调试-->选项-->反编译器 中设置相关选项,如下所示 这样就可以看到编译后生成的代码了.
围绕上面的示例我这里使用的 Debug模式 下编译生成的dll使用的 ILSpy 进行反编译,因为这里我需要让编译的源码看起来更清晰一点,而不是调试。如下所示首先看Main方法 。
//因为我们上面代码var taskOne = await TaskOne()
//使用了await语法糖,所以被替换成了状态机调用
[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;
//设置状态-1
stateMachine.<>1__state = -1;
//启动状态机
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
//这是系统默认帮我们生成的static void Main的入口方法
[SpecialName]
[DebuggerStepThrough]
private static void <Main>(string[] args)
{
//同步调用<Main>$方法
<Main>$(args).GetAwaiter().GetResult();
}
上面的代码就是编译器为我们生成的 Main 方法,通过这里我们可以得到两条信息 。
顶级语句
编译器会帮我们生成固定的入口函数格式,即 static void Main
这种标准格式 await
关键字则会编译出一段状态机相关的代码,把我们的逻辑放到编译的状态机类里 通过上面我们可以看到 <<Main>$>d__0 这个类是编译器帮我们生成的,我们可以看一下生成的代码 。
[CompilerGenerated]
private sealed class <<Main>$>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder <>t__builder;
public string[] args;
private string <taskOne>5__1;
private string <>s__2;
[System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })]
private TaskAwaiter<string> <>u__1;
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<string> awaiter;
//num的值来自<>1__state,由于在创建状态机的时候传递的是-1所以一定会走到这个逻辑
if (num != 0)
{
//调用TaskOne方法,也就是上面我们写的业务方法
//这个方法返回的是TaskAwaiter<>实例,以为我们TaskOne方法是异步方法
awaiter = <<Main>$>g__TaskOne|0_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<string>);
num = (<>1__state = -1);
}
//调用GetResult()方法获取异步执行结果
<>s__2 = awaiter.GetResult();
<taskOne>5__1 = <>s__2;
<>s__2 = null;
//这里对应我们上面的输出调用TaskOne方法的结果
Console.WriteLine(<taskOne>5__1);
Console.ReadLine();
}
catch (Exception exception)
{
<>1__state = -2;
<taskOne>5__1 = null;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<taskOne>5__1 = null;
<>t__builder.SetResult();
}
void IAsyncStateMachine.MoveNext()
{
this.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine)
{
}
void IAsyncStateMachine.SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine)
{
this.SetStateMachine(stateMachine);
}
}
这里的代码可以看到编译器生成的代码,其实这就是对应上面我们写的代码 。
var taskOne = await TaskOne();
Console.WriteLine(taskOne);
Console.ReadLine();
因为我们使用了 await 关键字,所以它帮我们生成了 IAsyncStateMachine 类,里面的核心逻辑咱们待会在介绍,因为今天的主题 TaskOne 方法还没介绍完成呢, TaskOne 生成的代码如下所示 。
//TaskOne方法编译时生成的代码
[CompilerGenerated]
private sealed class <<<Main>$>g__TaskOne|0_0>d : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder<string> <>t__builder;
private HttpResponseMessage <httpResponse>5__1;
private string <content>5__2;
private HttpResponseMessage <>s__3;
private string <>s__4;
[System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })]
private TaskAwaiter<HttpResponseMessage> <>u__1;
[System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })]
private TaskAwaiter<string> <>u__2;
private void MoveNext()
{
int num = <>1__state;
string result;
try
{
//因为我们使用了两次await所以这里会有两个TaskAwaiter<>实例
//var httpResponse = await ClassFactory.Client.GetAsync("https://www.cnblogs.com");
//var content = await httpResponse.Content.ReadAsStringAsync();
TaskAwaiter<string> awaiter;
TaskAwaiter<HttpResponseMessage> awaiter2;
if (num != 0)
{
if (num == 1)
{
awaiter = <>u__2;
<>u__2 = default(TaskAwaiter<string>);
num = (<>1__state = -1);
goto IL_0100;
}
//这段逻辑针对的是我们手写的这段代码
//await ClassFactory.Client.GetAsync("https://www.cnblogs.com")
awaiter2 = ClassFactory.Client.GetAsync("https://www.cnblogs.com").GetAwaiter();
//判断任务是否完成
if (!awaiter2.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter2;
<<<Main>$>g__TaskOne|0_0>d stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
return;
}
}
else
{
awaiter2 = <>u__1;
<>u__1 = default(TaskAwaiter<HttpResponseMessage>);
num = (<>1__state = -1);
}
//同步获取HttpResponseMessage结果实例
<>s__3 = awaiter2.GetResult();
<httpResponse>5__1 = <>s__3;
<>s__3 = null;
//这段代码对应生成的则是await httpResponse.Content.ReadAsStringAsync()
awaiter = <httpResponse>5__1.Content.ReadAsStringAsync().GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 1);
<>u__2 = awaiter;
<<<Main>$>g__TaskOne|0_0>d stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
goto IL_0100;
IL_0100:
//同步获取httpResponse.Content.ReadAsStringAsync()放的结果
<>s__4 = awaiter.GetResult();
<content>5__2 = <>s__4;
<>s__4 = null;
result = <content>5__2;
}
catch (Exception exception)
{
<>1__state = -2;
<httpResponse>5__1 = null;
<content>5__2 = null;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<httpResponse>5__1 = null;
<content>5__2 = null;
//调用AsyncTaskMethodBuilder<>方法放置httpResponse.Content.ReadAsStringAsync()结果
<>t__builder.SetResult(result);
}
void IAsyncStateMachine.MoveNext()
{
this.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine)
{
}
void IAsyncStateMachine.SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine)
{
this.SetStateMachine(stateMachine);
}
}
到这里为止,这些方法就是编译器帮我们生成的代码,也就是这些代码就在生成好的 dll 里的.
接下来我们分析一下状态机的调用过程,回到上面的 stateMachine.<>t__builder.Start(ref stateMachine) 这段状态机启动代码,我们跟进去看一下里面的逻辑 。
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[DebuggerStepThrough]
public void Start<[Nullable(0)] TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
//调用了AsyncMethodBuilderCore的Start方法并传递状态机实例
//即<<Main>$>d__0 stateMachine = new <<Main>$>d__0()实例
AsyncMethodBuilderCore.Start(ref stateMachine);
}
//AsyncMethodBuilderCore的Start方法
[DebuggerStepThrough]
public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
if (stateMachine == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
}
//获取当前线程实例
Thread currentThread = Thread.CurrentThread;
//获取当前执行上下文
ExecutionContext executionContext = currentThread._executionContext;
//获取当前同步上下文
SynchronizationContext synchronizationContext = currentThread._synchronizationContext;
try
{
//调用状态机的MoveNext方法
stateMachine.MoveNext();
}
finally
{
//执行完MoveNext之后
//还原SynchronizationContext同步上下文到当前实例
if (synchronizationContext != currentThread._synchronizationContext)
{
currentThread._synchronizationContext = synchronizationContext;
}
//还原ExecutionContext执行上下文到当前实例
ExecutionContext executionContext2 = currentThread._executionContext;
if (executionContext != executionContext2)
{
//执行完成之后把执行上下文装载到当前线程
ExecutionContext.RestoreChangedContextToThread(currentThread, executionContext, executionContext2);
}
}
}
执行完异步任务之后,会判断 SynchronizationContext 同步上下文环境和 ExecutionContext 执行上下文环境,保证异步异步之后的可以操作 UI线程 上的控件,或者异步的后续操作和之前的操作处在相同的执行上线文中.
题外话:ExecutionContext 是一个用于传递状态和环境信息的类,它可以在不同的执行上下文之间传递状态。执行上下文表示代码执行的环境,包括线程、应用程序域、安全上下文和调用上下文等。ExecutionContext 对象包含当前线程上下文的所有信息,如当前线程的安全上下文、逻辑执行上下文、同步上下文和物理执行上下文等。它提供了方法,可以将当前的执行上下文复制到另一个线程中,或者在异步操作之间保存和还原执行上下文。在异步编程中,使用 ExecutionContext 可以确保代码在正确的上下文中运行,并且传递必要的状态和环境信息.
SynchronizationContext 是一个用于同步执行上下文和处理 UI 线程消息循环的抽象类。它可以将回调方法派发到正确的线程中执行,避免了跨线程访问的问题,并提高了应用程序的响应性和可靠性。在异步编程中,可以使用 SynchronizationContext.Current 属性获取当前线程的同步上下文,并使用同步上下文的 Post 或 Send 方法将回调方法派发到正确的线程中执行.
由于调用 stateMachine.<>t__builder.Start(ref stateMachine) 传递的是 new <<Main>$>d__0() 实例,所以这里核心就是在调用生成的状态机 IAsyncStateMachine 实例,即我们上面的 <<Main>$>d__0 类的 MoveNext() 方法 。
void IAsyncStateMachine.MoveNext()
{
this.MoveNext();
}
由上面的代码可知,本质是调用的私有的 MoveNext() 方法,即会执行我们真实逻辑的那个方法。由于编译器生成的状态机代码的逻辑是大致相同的,所以我们直接来看,我们业务具体落实的代码即 <<<Main>$>g__TaskOne|0_0>d 状态机类里的,私有的那个 MoveNext 方法代码 。
AsyncTaskMethodBuilder<string> <>t__builder;
TaskAwaiter<HttpResponseMessage> awaiter2;
if (num != 0)
{
if (num == 1)
{}
//ClassFactory.Client.GetAsyn()方法生成的逻辑
awaiter2 = ClassFactory.Client.GetAsync("https://www.cnblogs.com").GetAwaiter();
if (!awaiter2.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter2;
<<<Main>$>g__TaskOne|0_0>d stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
return;
}
//同步获取异步结果
<>s__4 = awaiter.GetResult();
}
else
{}
TaskAwaiter<string> awaiter;
//httpResponse.Content.ReadAsStringAsync()方法生成的逻辑
awaiter = <httpResponse>5__1.Content.ReadAsStringAsync().GetAwaiter();
//判断任务是否完成
if (!awaiter.IsCompleted)
{
num = (<>1__state = 1);
<>u__2 = awaiter;
<<<Main>$>g__TaskOne|0_0>d stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
//同步获取异步结果,并将返回值装载
result= awaiter.GetResult();
<>t__builder.SetResult(result);
当然这里我们省了里面的很多逻辑,为了让结构看起来更清晰一点。 通过上面的它生成的结构来看,我们写代码的时候一个方法里的每个await都会被生成一个 TaskAwaiter 逻辑,根据当前异步状态 IsCompleted 判断任务是否完成,来执行下一步操作。如果任务未完成 IsCompleted为false 则调用 AsyncTaskMethodBuilder 实例的 AwaitUnsafeOnCompleted 方法,如果异步已完成则直接获取异步结果,进行下一步.
通过上面的逻辑我们可以看到,如果异步任务没有完成则调用了 AsyncTaskMethodBuilder 实例的 AwaitUnsafeOnCompleted 方法。接下来我们就看下 AwaitUnsafeOnCompleted 方法的实现 。
public void AwaitUnsafeOnCompleted<[Nullable(0)] TAwaiter, [Nullable(0)] TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
{
//调用AwaitUnsafeOnCompleted方法
AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task);
}
internal static void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref Task<TResult> taskField) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
{
//创建IAsyncStateMachineBox实例
IAsyncStateMachineBox stateMachineBox = GetStateMachineBox(ref stateMachine, ref taskField);
//调用AwaitUnsafeOnCompleted()方法
AwaitUnsafeOnCompleted(ref awaiter, stateMachineBox);
}
internal static void AwaitUnsafeOnCompleted<TAwaiter>(ref TAwaiter awaiter, IAsyncStateMachineBox box) where TAwaiter : ICriticalNotifyCompletion
{
//判断awaiter实例类型
if (default(TAwaiter) != null && awaiter is ITaskAwaiter)
{
//获取TaskAwaiter实例的m_task属性即Task类型
TaskAwaiter.UnsafeOnCompletedInternal(Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter).m_task, box, true);
return;
}
if (default(TAwaiter) != null && awaiter is IConfiguredTaskAwaiter)
{
//与上面逻辑一致m_task属性即Task类型本质他们都在操作Task
ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter reference = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
TaskAwaiter.UnsafeOnCompletedInternal(reference.m_task, box, reference.m_continueOnCapturedContext);
return;
}
if (default(TAwaiter) != null && awaiter is IStateMachineBoxAwareAwaiter)
{
try
{
//调用IStateMachineBoxAwareAwaiter实例的AwaitUnsafeOnCompleted方法
((IStateMachineBoxAwareAwaiter)(object)awaiter).AwaitUnsafeOnCompleted(box);
return;
}
catch (Exception exception)
{
System.Threading.Tasks.Task.ThrowAsync(exception, null);
return;
}
}
try
{
//调用ICriticalNotifyCompletion实例的UnsafeOnCompleted方法
awaiter.UnsafeOnCompleted(box.MoveNextAction);
}
catch (Exception exception2)
{
System.Threading.Tasks.Task.ThrowAsync(exception2, null);
}
}
通过这个方法我们可以看到传递进来的TAwaiter都是 ICriticalNotifyCompletion 的实现类,所以他们的行为存在一致性,只是具体的实现动作根据不同的实现类型来判断.
ITaskAwaiter
类的话直接调用 TaskAwaiter.UnsafeOnCompletedInternal()
方法,传递了 TaskAwaiter.m_task
属性,这是一个 Task
类型的属性 IConfiguredTaskAwaiter
类型的话,也是调用了 TaskAwaiter.UnsafeOnCompletedInternal()
方法,传递了 ConfiguredTaskAwaiter.m_task
属性,这也是一个 Task
类型的属性 IStateMachineBoxAwareAwaiter
类型的话,调用 IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted()
方法,传递的是当前的 IAsyncStateMachineBox
状态机盒子实例,具体实现咱们待会看 ICriticalNotifyCompletion.UnsafeOnCompleted()
方法,传递的是 IAsyncStateMachineBox.MoveNextAction
方法, IAsyncStateMachineBox
实现类包装了 IAsyncStateMachine
实现类,这里的 stateMachineBox.MoveNextAction
本质是在执行 IAsyncStateMachine的MoveNext
的方法,即我们状态机里我们自己写的业务逻辑。 我们首先来看一下 StateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted() 方法,找到一个实现类。因为它的实现类有好几个,比如 ConfiguredValueTaskAwaiter 、 ValueTaskAwaiter 、 YieldAwaitable 等,这里咱们选择有类型的 ConfiguredValueTaskAwaiter 实现类,看一下 AwaitUnsafeOnCompleted 方法 。
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
object? obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj is Task t)
{
//如果是Task类型的话会调用TaskAwaiter.UnsafeOnCompletedInternal方法,也是上面咱们多次提到的
TaskAwaiter.UnsafeOnCompletedInternal(t, box, _value._continueOnCapturedContext);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ThreadPool.s_invokeAsyncStateMachineBox, box, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
//兜底的方法也是TaskAwaiter.UnsafeOnCompletedInternal
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, _value._continueOnCapturedContext);
}
}
可以看到 ConfiguredValueTaskAwaiter.AwaitUnsafeOnCompleted() 方法最终也是执行到了 TaskAwaiter.UnsafeOnCompletedInternal() 方法,这个咱们上面已经多次提到了。接下里咱们再来看一下 ICriticalNotifyCompletion.UnsafeOnCompleted() 方法里的实现是啥,咱们找到它的一个常用的实现类,也是咱们上面状态机帮咱们生成的 TaskAwaiter<> 类里的实现 。
public void UnsafeOnCompleted(Action continuation)
{
TaskAwaiter.OnCompletedInternal(m_task, continuation, true, false);
}
//TaskAwaiter的OnCompletedInternal方法
internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext)
{
ArgumentNullException.ThrowIfNull(continuation, "continuation");
if (TplEventSource.Log.IsEnabled() || Task.s_asyncDebuggingEnabled)
{
continuation = OutputWaitEtwEvents(task, continuation);
}
//这里调用了Task的SetContinuationForAwait方法
task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext);
}
咱们看到了这里调用的是 Task的SetContinuationForAwait 方法,上面我们提到的 AwaitUnsafeOnCompleted 方法里直接调用了 TaskAwaiter 的 UnsafeOnCompletedInternal 方法,咱们可以来看一下里面的实现 。
internal static void UnsafeOnCompletedInternal(Task task, IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
if (TplEventSource.Log.IsEnabled() || Task.s_asyncDebuggingEnabled)
{
//默认情况下我们是没有去监听EventSource发布的时间消息
//如果你开启了EventSource日志的监听则会走到这里
task.SetContinuationForAwait(OutputWaitEtwEvents(task, stateMachineBox.MoveNextAction), continueOnCapturedContext, false);
}
else
{
task.UnsafeSetContinuationForAwait(stateMachineBox, continueOnCapturedContext);
}
}
因为默认是没有开启 EventSource 的监听,所以上面的两个 TplEventSource.Log.IsEnabled 相关的逻辑执行不到,如果代码里坚挺了相关的 EventSource 则会执行这段逻辑。 SetContinuationForAwait 方法和 UnsafeSetContinuationForAwait 方法逻辑是一致的,只是因为如果开启了 EventSource 的监听会发布事件消息,其中包装了关于异步信息的事件相关。所以我们可以直接来看 UnsafeSetContinuationForAwait 方法实现 。
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
if (continueOnCapturedContext)
{
//winform wpf等ui线程包含同步上下文SynchronizationContext相关的信息
//如果存在则直接在SynchronizationContext同步上线文中的Post方法把异步结果在ui线程中完成回调执行
SynchronizationContext current = SynchronizationContext.Current;
if (current != null && current.GetType() != typeof(SynchronizationContext))
{
SynchronizationContextAwaitTaskContinuation synchronizationContextAwaitTaskContinuation = new SynchronizationContextAwaitTaskContinuation(current, stateMachineBox.MoveNextAction, false);
if (!AddTaskContinuation(synchronizationContextAwaitTaskContinuation, false))
{
synchronizationContextAwaitTaskContinuation.Run(this, false);
}
return;
}
//判断是否包含内部任务调度器,如果不是默认的TaskScheduler.Default调度策略,也就是ThreadPoolTaskScheduler的方式执行MoveNext
//则使用TaskSchedulerAwaitTaskContinuation的Run方法执行MoveNext
TaskScheduler internalCurrent = TaskScheduler.InternalCurrent;
if (internalCurrent != null && internalCurrent != TaskScheduler.Default)
{
TaskSchedulerAwaitTaskContinuation taskSchedulerAwaitTaskContinuation = new TaskSchedulerAwaitTaskContinuation(internalCurrent, stateMachineBox.MoveNextAction, false);
if (!AddTaskContinuation(taskSchedulerAwaitTaskContinuation, false))
{
taskSchedulerAwaitTaskContinuation.Run(this, false);
}
return;
}
}
//执行兜底逻辑使用线程池执行
if (!AddTaskContinuation(stateMachineBox, false))
{
ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, true);
}
}
上面我们提到过 IAsyncStateMachineBox 实现类包装了 IAsyncStateMachine 实现类,它的 stateMachineBox.MoveNextAction 本质是在执行 AsyncStateMachine的MoveNext 的方法,即我们状态机里的自己的业务逻辑。根据上面的逻辑我们来大致总结一下相关的执行策略 。
SynchronizationContext
同步上下文,也就是winform wpf等ui线程,则直接在SynchronizationContext同步上线文中的Post方法把异步结果在ui线程中完成回调执行,里面的核心方法咱们待会会看到 TaskScheduler
调度器不是默认的 ThreadPoolTaskScheduler
调度器,则使用自定义的TaskScheduler来执行MoveNext方法,统一里面的核心方法咱们待来看 ThreadPool的UnsafeQueueUserWorkItemInternal
方法 好了上面留下了两个核心的方法,没有展示相关的实现,首先咱们来看下 TaskSchedulerAwaitTaskContinuation的Run 方法,这个方法适用于存在同步上下文的场景,来看下它的核心逻辑 。
internal sealed override void Run(Task task, bool canInlineContinuationTask)
{
//判断当前线程同步上下文是否和传递的同步上下文一致,则直接执行,说明当前线程可以直接使用异步结果
if (canInlineContinuationTask && m_syncContext == SynchronizationContext.Current)
{
RunCallback(AwaitTaskContinuation.GetInvokeActionCallback(), m_action, ref Task.t_currentTask);
return;
}
//如果不是同一个同步上下文则执行PostAction委托
RunCallback(PostAction, this, ref Task.t_currentTask);
}
private static void PostAction(object state)
{
//通过传递的state来捕获执行回调的同步上下文,这里使用的SynchronizationContext的非阻塞的Post方法来执行后续逻辑
SynchronizationContextAwaitTaskContinuation synchronizationContextAwaitTaskContinuation = (SynchronizationContextAwaitTaskContinuation)state;
synchronizationContextAwaitTaskContinuation.m_syncContext.Post(s_postCallback, synchronizationContextAwaitTaskContinuation.m_action);
}
protected void RunCallback(ContextCallback callback, object state, ref Task currentTask)
{
//捕获执行上下文,异步执行完成之后在执行上下文中执行后续逻辑
ExecutionContext capturedContext = m_capturedContext;
if (capturedContext == null)
{
//核心逻辑就是再行上面的委托即AwaitTaskContinuation.GetInvokeActionCallback方法或PostAction方法
callback(state);
}
else
{
ExecutionContext.RunInternal(capturedContext, callback, state);
}
}
上面的方法省略了一些逻辑,为了让逻辑看起来更清晰,我们可以看到里面的逻辑,即在同步上下文 SynchronizationContext 中执行异步的回调的结果。如果当前线程就包含同步上下文则直接执行,如果不是则使用之前传递进来的同步上下文来执行。执行的时候会尝试捕获执行上下文。咱们还说到了如果 TaskScheduler 调度器不是默认的 ThreadPoolTaskScheduler 调度器,则使用自定义的TaskScheduler来执行MoveNext方法,来看下里面的核心实现 。
internal sealed override void Run(Task ignored, bool canInlineContinuationTask)
{
//如果当前的scheduler策略是TaskScheduler.Default即默认的ThreadPoolTaskScheduler
//则直接使用默认策略调度任务
if (m_scheduler == TaskScheduler.Default)
{
base.Run(ignored, canInlineContinuationTask);
return;
}
//如果不是默认策略则使用,我们定义的TaskScheduler
Task task = CreateTask(delegate(object state)
{
try
{
((Action)state)();
}
catch (Exception exception)
{
Task.ThrowAsync(exception, null);
}
}, m_action, m_scheduler);//这里的m_scheduler指的是自定义的TaskScheduler
bool flag = canInlineContinuationTask && (TaskScheduler.InternalCurrent == m_scheduler || Thread.CurrentThread.IsThreadPoolThread);
//或者是task其他形式的策略执行
if (flag)
{
TaskContinuation.InlineIfPossibleOrElseQueue(task, false);
return;
}
try
{
task.ScheduleAndStart(false);
}
catch (TaskSchedulerException)
{
}
}
这个逻辑看起来比较清晰,即根据Task的执行策略 TaskScheduler 判断如何执行任务,比如默认的 ThreadPoolTaskScheduler 策略,或其他策略,比如单线程策略或者自定义的等等。 上面的执行过程可以总结为以下两点 。
Task
调度,否则执行默认的 ThreadPool.UnsafeQueueUserWorkItemInternal()
执行。如果是 TaskScheduler
则判断是哪一种策略,比如是默认的 ThreadPoolTaskScheduler
或是其它策略亦或是自定义策略等。 SynchronizationContext
,比如UI线程,大家都知道修改界面控件需要在UI线程上才能执行,但是 await
操作可能存在线程切换如果await的结果需要在UI展示需要同步上下文保证异步的结果在UI线程中执行。 如果任务需要执行中,我们总得想办法把结果给相应的 Task 实例,这样我们才能在执行完成之后把得到对应的执行状态或者执行结果在相关的Task中体现出来,方便我们判断Task是否执行完成或者获取相关的执行结果,在 ThreadPoolWorkQueue 中有相关的逻辑具体在 DispatchWorkItem 方法中 。
private static void DispatchWorkItem(object workItem, Thread currentThread)
{
//判断在线程池中自行的任务书否是Task任务
Task task = workItem as Task;
if (task != null)
{
task.ExecuteFromThreadPool(currentThread);
}
else
{
Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
}
}
ThreadPool 里的线程执行了 Task的ExecuteWithThreadLocal 的方法,核心执行方法在 Task的ExecuteWithThreadLocal ,这样的话执行相关的结果就可以体现在 Task 实例中,比如 Task的IsCompleted 属性判断是否执行完成,或者 Task<TResult>的GetResult f方法获取结果等等.
这里需要注意的是 Task.FromResult<TResult>(TResult) 这个方法,相信大家经常用到,如果你的执行结果需要包装成 Task<TResult> 总会用到这个方法。它的意思是 创建一个Task<TResult>,并以指定结果成功完成。 ,也就是 Task<TResult>的IsCompleted属性为true ,这个结论可以在 dotnet api 中 Task.FromResult (TResult) 文档中看到,因为我们只需要把我们已有的结果包装成Task所以不涉及到复杂的执行,这也意味着在生成状态机的时候 MoveNext 方法里的逻辑判断 IsCompleted 时候代表任务是直接完成的,会直接通过 GetResult() 获取到结果,不需要 AwaitUnsafeOnCompleted 去根据执行策略执行 。
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<string> awaiter;
if (num != 0)
{
awaiter = Task.FromResult("Hello World").GetAwaiter();
//这里的IsCompleted会为true不会执行相关的执行策略
if (!awaiter.IsCompleted)
{
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
return;
}
}
else
{
}
<>s__2 = awaiter.GetResult();
}
catch (Exception exception)
{
}
<>t__builder.SetResult();
}
本文主要是展示了近期对 async和await 生成的状态机的研究,大概了解了相关的执行过程。由于异步编程涉及到的东西比较多,而且相当复杂,足够写一本书。所以本文设计到的不过是一些皮毛,也由于本人能力有限理解的不一定对,还望谅解。通过本文大家知道 async和await 是语法糖,会生成状态机相关代码,让我们来总结一下 。
async和await
是语法糖,会生成状态机类并填充我们编写的业务代码相关 IsCompleted为false
则会执行相关的逻辑去执行任务
Task
调度,否则执行默认的 ThreadPool.UnsafeQueueUserWorkItemInternal()
执行。如果是 TaskScheduler
则判断是哪一种策略,比如是默认的 ThreadPoolTaskScheduler
或是其它策略亦或是自定义策略等。 SynchronizationContext
,比如UI线程,大家都知道修改界面控件需要在UI线程上才能执行,但是 await
操作可能存在线程切换如果await的结果需要在UI展示需要同步上下文保证异步的结果在UI线程中执行。 Task.FromResult<TResult>(TResult)
这个方法,它的意思是 创建一个Task<TResult>,并以指定结果成功完成。
,也就是 Task<TResult>的IsCompleted属性为true
。 结论只涉及到了 async和await 语法糖生成的状态机相关,不涉及到关于异步或者同步相关的知识点,因为说到这些话题就变得很大了,还望谅解.
最近看到许多关于裁员跳槽甚至是换行的,每个人都有自己的生活,都有自己的处境,所以有些行为我们要换位思考,理解他们选择生活的方式,每个人能得到自己想要的,能开心就好,毕竟精力有限,为了最想要的总要舍弃一些.
最后此篇关于研究c#异步操作asyncawait状态机的总结的文章就讲到这里了,如果你想了解更多关于研究c#异步操作asyncawait状态机的总结的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我即将在 uni 开始关于 OpenCL 的荣誉项目,以及如何使用它来改进现代游戏开发。我知道现在/很快有几本书关于学习 opencl,但我想知道是否有人知道关于 opencl 的任何好论文。 我一直
在 Web 开发领域,我听过很多博客,人们说用户倾向于忽略(或被激怒)华而不实/不必要的动画内容,对可用性产生不利影响,但是否有任何研究支持那个声明? 最佳答案 我找到了一对似乎符合您正在寻找的路线。
我对网络语音和音频不熟悉。 我一直在谷歌上搜索示例和解决方案来实现 p2p 语音连接(这个想法是音频 session )。 我发现 google talk xmpp 框架可以访问用户和文本聊天等信息。
我打算创建一个优化的数据结构来保存汇编代码。这样我就可以完全负责将在这个结构上工作的优化算法。如果我可以边运行边编译。这将是一种动态执行。这可能吗?有没有人见过这样的事情? 我应该使用结构将结构链接到
低代码开发平台 Mendix 最新发布的一份研究报告表明,疫情在全球范围内的扩散大大加速了对企业低代码的兴趣和使用。该报告基于来自美国、中国、英国、德国、比利时和荷兰的 1209 名 IT 专业人士
最常用的数值类型是int,但是它未必是最佳选择。bigint,smallint,tinyint可以应用在特殊场合。他们的特性如下表所示: Data type
我试图了解在使用 R 包 kernlab 中的命令 ksvm 时 SVM 预测函数是如何工作的。 我尝试使用以下命令查看预测函数: methods(class="ksvm") getAnywhere(
据我了解,XMPP 协议(protocol)基于始终在线的连接,您无法立即指示 XML 消息何时结束。 这意味着您必须在流来时对其进行评估。这也意味着,您可能必须处理异步连接,因为套接字可能会阻塞在
两天后,我发现自己正在考虑通过网站上的 ajax 请求实现流程自动化。我想做的是研究网站上执行的某些操作的一些请求,并用 javascript 复制它。例如,当我在剧院选择一些座位时,我有这个 POS
当我研究我没有编写的复杂 Java 代码时,我会定期在记事本中写下一些我对给定问题感兴趣的资源的调用堆栈。这些资源通常是方法或常量。 这是一个例子: CONSTANT_NAME com.com
我将为 Android 智能手机实现一个蜜 jar 作为我论文的研究。我从来没有使用过android、java和蜜 jar 。这就是我想开始的方式。我想到使用Honeyd的源代码(用C语言编写),Ho
Closed. This question does not meet Stack Overflow guidelines 。它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 Stack Ov
晚上好。 我真的很难理解这个问题,我不确定我是否错过了一些非常愚蠢的东西,但这是我的代码和我的问题。 const question = new Map(); question.set('questio
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 10 年前。 Improve thi
我想知道是否有人有任何关于编译/链接优化的好资源(论文/文章/书籍引用)。 我曾在两家以不同方式执行链接操作的公司工作。 第一公司强制代码采用严格的 DAG 结构,向我解释说使用强制树结构,链接时间非
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve thi
我有 4 个文件想用 Python/Pandas 读取,这些文件是: https://github.com/kelsey9649/CS8370Group/tree/master/TaFengDataS
我正在尝试查找任何分析 Qt 和 Qt Creator 最新版本的研究/学术/期刊论文/文章。 具体来说,我试图从实时安全关键的角度评估 Qt,所以任何信息都是有帮助的。 附言我尝试了典型的搜索方法:
在 Main Wrapper 中,我有 5 个 Div。第一个 div 包含 4 个 Box(box_1, box_2,box_3,box_4),我的点击事件将在其中发生。 另外4个div在main
所以,我一直在用 Netbeans C++ 调试一个程序,我需要仔细查看 vector 的内容,以帮助我找出哪里出了问题。因此,我进入“变量”选项卡进行挖掘。问题是,Netbeans(或我使用过的任何
我是一名优秀的程序员,十分优秀!