请问关于c的具体应用场景有哪些?

2026-04-24 15:222阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计4087个文字,预计阅读时间需要17分钟。

请问关于c的具体应用场景有哪些?

目录+前言+async+await是语法糖+生成的状态机+启动状态机+执行异步任务+线程池和Task关联+Task的FromResult+总结+前言+前一段空闲的时间优化了我之前轮子的DotNetCoreRpc小框架。

目录
  • 前言
  • async await是语法糖
  • 生成的状态机
  • 启动状态机
  • 执行异步任务
  • 线程池和Task关联
  • Task的FromResult
  • 总结

请问关于c的具体应用场景有哪些?

前言

前一段时间得闲的时候优化了一下我之前的轮子[DotNetCoreRpc]小框架,其中主要的优化点主要是关于RPC异步契约调用的相关逻辑。在此过程中进一步了解了关于async和await异步操作相关的知识点,加深了异步操作的理解,因此总结一下。关于async和await每个人都有自己的理解,甚至关于异步和同步亦或者关于异步和多线程每个人也都有自己的理解。因此,如果本文涉及到个人观点与您的观点不一致的时候请勿喷。结论固然重要,但是在这个过程中的引发的思考也很重要。

async await是语法糖

大家应该都比较清楚async和await这对关键字是一组语法糖,关于语法糖大家可以理解为,编码过程中写了一个关键字,但是编译的时候会把它编译成别的东西,主要是用来提升开发效率。比如我有一段关于async和await相关的代码,如下所示

var taskOne = await TaskOne(); Console.WriteLine(taskOne); Console.ReadLine(); static async Task<string> TaskOne() { var www.cnblogs.com"); var content = await www.cnblogs.com"); //var content = await www.cnblogs.com") awaiter2 = ClassFactory.Client.GetAsync("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(); <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()方法,找到一个实现类。因为它的实现类有好几个,比如ConfiguredValueTaskAwaiterValueTaskAwaiterYieldAwaitable等,这里咱们选择有类型的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方法里直接调用了TaskAwaiterUnsafeOnCompletedInternal方法,咱们可以来看一下里面的实现

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中体现出来,方便我们判断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>的GetResultf方法获取结果等等。

Task的FromResult

这里需要注意的是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语法糖生成的状态机相关,不涉及到关于异步或者同步相关的知识点,因为说到这些话题就变得很大了,还望谅解。

最近看到许多关于裁员跳槽甚至是换行的,每个人都有自己的生活,都有自己的处境,所以有些行为我们要换位思考,理解他们选择生活的方式,每个人能得到自己想要的,能开心就好,毕竟精力有限,为了最想要的总要舍弃一些。

标签:总结推荐

本文共计4087个文字,预计阅读时间需要17分钟。

请问关于c的具体应用场景有哪些?

目录+前言+async+await是语法糖+生成的状态机+启动状态机+执行异步任务+线程池和Task关联+Task的FromResult+总结+前言+前一段空闲的时间优化了我之前轮子的DotNetCoreRpc小框架。

目录
  • 前言
  • async await是语法糖
  • 生成的状态机
  • 启动状态机
  • 执行异步任务
  • 线程池和Task关联
  • Task的FromResult
  • 总结

请问关于c的具体应用场景有哪些?

前言

前一段时间得闲的时候优化了一下我之前的轮子[DotNetCoreRpc]小框架,其中主要的优化点主要是关于RPC异步契约调用的相关逻辑。在此过程中进一步了解了关于async和await异步操作相关的知识点,加深了异步操作的理解,因此总结一下。关于async和await每个人都有自己的理解,甚至关于异步和同步亦或者关于异步和多线程每个人也都有自己的理解。因此,如果本文涉及到个人观点与您的观点不一致的时候请勿喷。结论固然重要,但是在这个过程中的引发的思考也很重要。

async await是语法糖

大家应该都比较清楚async和await这对关键字是一组语法糖,关于语法糖大家可以理解为,编码过程中写了一个关键字,但是编译的时候会把它编译成别的东西,主要是用来提升开发效率。比如我有一段关于async和await相关的代码,如下所示

var taskOne = await TaskOne(); Console.WriteLine(taskOne); Console.ReadLine(); static async Task<string> TaskOne() { var www.cnblogs.com"); var content = await www.cnblogs.com"); //var content = await www.cnblogs.com") awaiter2 = ClassFactory.Client.GetAsync("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(); <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()方法,找到一个实现类。因为它的实现类有好几个,比如ConfiguredValueTaskAwaiterValueTaskAwaiterYieldAwaitable等,这里咱们选择有类型的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方法里直接调用了TaskAwaiterUnsafeOnCompletedInternal方法,咱们可以来看一下里面的实现

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中体现出来,方便我们判断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>的GetResultf方法获取结果等等。

Task的FromResult

这里需要注意的是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语法糖生成的状态机相关,不涉及到关于异步或者同步相关的知识点,因为说到这些话题就变得很大了,还望谅解。

最近看到许多关于裁员跳槽甚至是换行的,每个人都有自己的生活,都有自己的处境,所以有些行为我们要换位思考,理解他们选择生活的方式,每个人能得到自己想要的,能开心就好,毕竟精力有限,为了最想要的总要舍弃一些。

标签:总结推荐