async与await 浅谈

简介

.net4.5(C#5.0)之后,两个异步语法糖——让异步逻辑成为了近似线性的流程。




基本语法

  1.  关键字:方法头使用 async 修饰。

  2.  要求:包含 N(N>0) 个 await 表达式(不存在 await 表达式的话 IDE 会发出警告),表示需要异步执行的任务。

  3.  返回类型:只能返回 3 种类型(void、Task 和Task<T>)。Task 和Task<T> 标识返回的对象会在将来完成工作,表示调用方法和异步方法可以继续执行。

  4.  参数:数量不限,但不能使用 out 和 ref 关键字。

  5.  命名约定:方法后缀名应以 Async 结尾。

  6.  其它:匿名方法和 Lambda 表达式也可以作为异步对象;async 是一个上下文关键字;关键字 async 必须在返回类型前。



控制流


解释:
  1. 事件处理程序调用并等待 AccessTheWebAsync() 异步方法。

  2. AccessTheWebAsync创建 HttpClient 对象并调用它的 GetStringAsync 异步方法来下载网站内容。

  3. 假设 GetStringAsync 中发生了某种情况,该情况挂起了它的进程。可能必须等待网站下载或一些其他阻塞的活动。为避免阻塞资源,GetStringAsync() 会将控制权出让给其调用方 AccessTheWebAsync。GetStringAsync 返回 Task,其中 TResult 为字符串,并且 AccessTheWebAsync 将任务分配给 getStringTask 变量在工作完成时接收实际字符串值。

  4. 由于尚未等待 getStringTask,因此,AccessTheWebAsync 可以继续执行不依赖于 GetStringAsync 得出最终结果的其他任务。该任务由对同步方法 DoIndependentWork 的调用表示。

  5. DoIndependentWork 是完成其工作并返回其调用方的同步方法。

  6. AccessTheWebAsync 已完成工作,可以不受 getStringTask 的结果影响。 接下来,AccessTheWebAsync 需要计算并返回该下载字符串的长度,但该方法仅在具有字符串时才能计算该值。因此,AccessTheWebAsync 使用一个 await 运算符来挂起其进度,并把控制权交给调用 AccessTheWebAsync 的方法。AccessTheWebAsync 将 Task<int> 返回至调用方。 该任务产生下载字符串长度返回。如果 GetStringAsync(即 getStringTask)在 AccessTheWebAsync 等待前完成,则控制权会保留在 AccessTheWebAsync 中。 如果异步调用过程 (getStringTask) 已完成,并且 AccessTheWebSync 不必等待最终结果,则挂起然后返回到 AccessTheWebAsync,但这会造成成本的浪费。

  7. GetStringAsync 完成并生成一个字符串结果。 字符串结果不是通过你预期的方式调用 GetStringAsync 所返回的。(此方法已在步骤 3 中返回一个任务。)相反,字符串结果存储在表示完成方法 getStringTask 的任务中。 await 运算符从 getStringTask 中检索结果。赋值语句将检索到的结果赋给 urlContents

     AccessTheWebAsync 具有字符串结果时,该方法可以计算字符串长度。然后,AccessTheWebAsync 工作也将完成,并且等待事件处理程序可继续使用。


编译流

 以一个简单的指控台程序为例

static void Main(string[] args)
{
    new Program().SampleMethodAsync();
    Console.WriteLine("THERE");
Console.Read();} async void SampleMethodAsync() {
    Console.WriteLine(await GetHere());
}

 Task<string> GetHere()
{
    return Task.Run(() =>
    {
        Thread.Sleep(1000);
        return "HERE";
    });
}

从ILSPY中我们可以看出async方法中的代码会被编译至

// System.Runtime.CompilerServices.AsyncMethodBuilderCore
[DebuggerStepThrough, SecuritySafeCritical]
internal void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
    if (stateMachine == null)
    {
        throw new ArgumentNullException("stateMachine");
    }
    Thread currentThread = Thread.CurrentThread;
    ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
    RuntimeHelpers.PrepareConstrainedRegions();
    try
    {
        ExecutionContext.EstablishCopyOnWriteScope(currentThread, false, ref executionContextSwitcher);
        stateMachine.MoveNext();
    }
    finally
    {
        executionContextSwitcher.Undo(currentThread);
    }
}

注:stateMachine就是SampleMethod方法编译时生成的内部结构体


再看MoveNext的具体实现

bool '<>t__doFinallyBodies';
Exception '<>t__ex';
int CS$0$0000;
TaskAwaiter<string> CS$0$0001;
TaskAwaiter<string> CS$0$0002;

try
{
    '<>t__doFinallyBodies' = true;
    CS$0$0000 = this.'<>1__state';
    if (CS$0$0000 != 0)
    {
        CS$0$0001 = this.'<>4__this'.GetHere().GetAwaiter();
        if (!CS$0$0001.IsCompleted)
        {
            this.'<>1__state' = 0;
            this.'<>u__$awaiter1' = CS$0$0001;
            this.'<>t__builder'.AwaitUnsafeOnCompleted(ref CS$0$0001, ref this);
            '<>t__doFinallyBodies' = false;
            return;
        }
    }
    else
    {
        CS$0$0001 = this.'<>u__$awaiter1';
        this.'<>u__$awaiter1' = CS$0$0002;
        this.'<>1__state' = -1;
    }

    Console.WriteLine(CS$0$0001.GetResult());
}

  1. GetHere()返回Task<string>
  2. GetAwaiter()返回TaskAwaiter<string>

注:TaskAwaiter的属性,可以看出我们可以对异步方法进一步扩展

public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
    {

        public bool IsCompleted { get; }
        public void GetResult();
        public void OnCompleted(Action continuation);
        public void UnsafeOnCompleted(Action continuation);
    }

// System.Runtime.CompilerServices.AsyncVoidMethodBuilder
[__DynamicallyInvokable, SecuritySafeCritical]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
    ref TAwaiter awaiter, ref TStateMachine stateMachine)
    where TAwaiter : ICriticalNotifyCompletion
    where TStateMachine : IAsyncStateMachine
{
    try
    {
        Action completionAction = this.m_coreState
            .GetCompletionAction<AsyncVoidMethodBuilder, TStateMachine>(ref this, ref stateMachine);
        awaiter.UnsafeOnCompleted(completionAction);
    }
    catch (Exception exception)
    {
        AsyncMethodBuilderCore.ThrowAsync(exception, null);
    }
}
  1. 创建了一个Action,MoveNext方法的信息已经随着stateMachine被封装进去了。
  2. 把Action交给Awaiter,让它在await的操作完成后执行这个Action。
    
// System.Runtime.CompilerServices.AsyncMethodBuilderCore
[SecuritySafeCritical]
internal Action GetCompletionAction<TMethodBuilder, TStateMachine>(ref TMethodBuilder builder, ref TStateMachine stateMachine)
    where TMethodBuilder : IAsyncMethodBuilder
    where TStateMachine : IAsyncStateMachine
{
    Debugger.NotifyOfCrossThreadDependency();
    ExecutionContext executionContext = ExecutionContext.FastCapture();
    Action action;
    AsyncMethodBuilderCore.MoveNextRunner moveNextRunner;
    if (executionContext != null && executionContext.IsPreAllocatedDefault)
    {
        action = this.m_defaultContextAction;
        if (action != null)
        {
            return action;
        }
        moveNextRunner = new AsyncMethodBuilderCore.MoveNextRunner(executionContext);
        action = new Action(moveNextRunner.Run);
        if (AsyncCausalityTracer.LoggingOn)
        {
            action = (this.m_defaultContextAction = this.OutputAsyncCausalityEvents<TMethodBuilder>(ref builder, action));
        }
        else
        {
            this.m_defaultContextAction = action;
        }
    }
    else
    {
        moveNextRunner = new AsyncMethodBuilderCore.MoveNextRunner(executionContext);
        action = new Action(moveNextRunner.Run);
        if (AsyncCausalityTracer.LoggingOn)
        {
            action = this.OutputAsyncCausalityEvents<TMethodBuilder>(ref builder, action);
        }
    }
    if (this.m_stateMachine == null)
    {
        builder.PreBoxInitialization<TStateMachine>(ref stateMachine);
        this.m_stateMachine = stateMachine;
        this.m_stateMachine.SetStateMachine(this.m_stateMachine);
    }
    moveNextRunner.m_stateMachine = this.m_stateMachine;
    return action;
}

  1. MoveNextRunner的Run方法被封装到了返回的Action里。
  2. Run方法里面有两条分支:
    1.直接调用MoveNext
    2.通过InvokeMoveNext调用MoveNext



执行action

// System.Threading.Tasks.Task
[SecurityCritical]
internal void SetContinuationForAwait(
    Action continuationAction,
    bool continueOnCapturedContext,
    bool flowExecutionContext,
    ref StackCrawlMark stackMark)
{
    TaskContinuation taskContinuation = null;
    if (continueOnCapturedContext)
    {
        SynchronizationContext currentNoFlow = SynchronizationContext.CurrentNoFlow;
        if (currentNoFlow != null && currentNoFlow.GetType() != typeof(SynchronizationContext))
        {
            taskContinuation = new SynchronizationContextAwaitTaskContinuation(
                currentNoFlow, continuationAction, flowExecutionContext, ref stackMark);
        }
        else
        {
            TaskScheduler internalCurrent = TaskScheduler.InternalCurrent;
            if (internalCurrent != null && internalCurrent != TaskScheduler.Default)
            {
                taskContinuation = new TaskSchedulerAwaitTaskContinuation(
                    internalCurrent, continuationAction, flowExecutionContext, ref stackMark);
            }
        }
    }
    if (taskContinuation == null && flowExecutionContext)
    {
        taskContinuation = new AwaitTaskContinuation(continuationAction, true, ref stackMark);
    }
    if (taskContinuation != null)
    {
        if (!this.AddTaskContinuation(taskContinuation, false))
        {
            taskContinuation.Run(this, false);
            return;
        }
    }
    else if (!this.AddTaskContinuation(continuationAction, false))
    {
        AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
    }
}
// System.Threading.Tasks.AwaitTaskContinuation
[SecurityCritical]
internal static void UnsafeScheduleAction(Action action, Task task)
{
    AwaitTaskContinuation awaitTaskContinuation = new AwaitTaskContinuation(action, false);
    TplEtwProvider log = TplEtwProvider.Log;
    if (log.IsEnabled() && task != null)
    {
        awaitTaskContinuation.m_continuationId = Task.NewId();
        log.AwaitTaskContinuationScheduled(
            (task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id,
            task.Id,
            awaitTaskContinuation.m_continuationId);
    }
    ThreadPool.UnsafeQueueCustomWorkItem(awaitTaskContinuation, false);
}

// System.Threading.ThreadPool
[SecurityCritical]
internal static void UnsafeQueueCustomWorkItem(IThreadPoolWorkItem workItem, bool forceGlobal)
{
    ThreadPool.EnsureVMInitialized();
    try
    {
    }
    finally
    {
        ThreadPoolGlobals.workQueue.Enqueue(workItem, forceGlobal);
    }
}

QueueUserWorkItem方法内部调用了ThreadPoolGlobals.workQueue.Enqueue——异步的方法就是在这里被执行。




再来看一下一种情况

async void SampleMethod()
{
    Console.WriteLine("WHERE");
    Console.WriteLine(await GetHere());
}


此时的MoveNext

try
{
    '<>t__doFinallyBodies' = true;
    CS$0$0000 = this.'<>1__state';
    if (CS$0$0000 != 0)
    {
        Console.WriteLine("WHERE");
        CS$0$0001 = this.'<>4__this'.GetHere().GetAwaiter();
        if (!CS$0$0001.IsCompleted)
        {
            this.'<>1__state' = 0;
            this.'<>u__$awaiter1' = CS$0$0001;
            this.'<>t__builder'.AwaitUnsafeOnCompleted(ref CS$0$0001, ref this);
            '<>t__doFinallyBodies' = false;
            return;
        }
    }
    else
    {
        CS$0$0001 = this.'<>u__$awaiter1';
        this.'<>u__$awaiter1' = CS$0$0002;
        this.'<>1__state' = -1;
    }

    Console.WriteLine(CS$0$0001.GetResult());
}

可以看出来await前后的代码被放到了两个区块里,而这两个区块,也就是MoveNext的两个分支





总结

  1. async方法中的代码会被移交给IAsyncStateMachine(状态机)的MoveNext方法。
  2. async方法中await操作前后的代码被分离,无论方法是同步还是异步都可以用async关键字来进行标识,因为用async标识只是显示表明在该方法内可能会用到await关键字使其变为异步方法,而且将该异步方法进行了明确的划分,只有用了await关键字时才是异步操作,其余一并为同步操作

  3. 主线程直接执行await前的代码(同步),并将await的Task移交给线程池ThreadPoolGlobal(异步)。
  4. 子线程执行完主线程递交来的Task后,再次走入MoveNext方法,执行await后的代码。(同步或异步)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值