在异步方法中省略异步和等待[重复]

Posted

技术标签:

【中文标题】在异步方法中省略异步和等待[重复]【英文标题】:eliding async and await in async methods [duplicate] 【发布时间】:2018-11-01 00:36:58 【问题描述】:

一个简单的问题;阅读本文:http://blog.stephencleary.com/2016/12/eliding-async-await.html

它通常告诉我,使用 async/await。已经在这样做了。但是,他还说在代理任务时不必使用异步部分。

// Simple passthrough to next layer: elide.
Task<string> PassthroughAsync(int x) => _service.DoSomethingPrettyAsync(x);

// Simple overloads for a method: elide.
async Task<string> DoSomethingPrettyAsync(CancellationToken cancellationToken)

    ... // Core implementation, using await.

为什么在通过时不应该使用 async/await?是不是不太方便,还有意义吗?

有什么想法吗?

【问题讨论】:

您链接到的文章描述了所有这些。删除是一种优化。 在这种情况下使用异步等待如何“更方便”? 【参考方案1】:

为什么在通过时不应该使用async/await?

因为在您键入 await 的那一刻,编译器会添加 ton 的实现胶水,它对您完全没有任何作用 - 调用者已经可以等待代理任务.

如果我添加类似于您的 PassthroughAsync 的内容,但使用 async/await

async Task<string> AwaitedAsync(int x) => await DoSomethingPrettyAsync(x);

然后我们可以通过编译和反编译IL来查看巨大但完全冗余的代码:

[AsyncStateMachine(typeof(<AwaitedAsync>d__1))]
private Task<string> AwaitedAsync(int x)

    <AwaitedAsync>d__1 <AwaitedAsync>d__ = default(<AwaitedAsync>d__1);
    <AwaitedAsync>d__.<>4__this = this;
    <AwaitedAsync>d__.x = x;
    <AwaitedAsync>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
    <AwaitedAsync>d__.<>1__state = -1;
    AsyncTaskMethodBuilder<string> <>t__builder = <AwaitedAsync>d__.<>t__builder;
    <>t__builder.Start(ref <AwaitedAsync>d__);
    return <AwaitedAsync>d__.<>t__builder.Task;

[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <AwaitedAsync>d__1 : IAsyncStateMachine

    public int <>1__state;

    public AsyncTaskMethodBuilder<string> <>t__builder;

    public C <>4__this;

    public int x;

    private TaskAwaiter<string> <>u__1;

    private void MoveNext()
    
        int num = <>1__state;
        C c = <>4__this;
        string result;
        try
        
            TaskAwaiter<string> awaiter;
            if (num != 0)
            
                awaiter = c.DoSomethingPrettyAsync(x).GetAwaiter();
                if (!awaiter.IsCompleted)
                
                    num = (<>1__state = 0);
                    <>u__1 = awaiter;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                    return;
                
            
            else
            
                awaiter = <>u__1;
                <>u__1 = default(TaskAwaiter<string>);
                num = (<>1__state = -1);
            
            result = awaiter.GetResult();
        
        catch (Exception exception)
        
            <>1__state = -2;
            <>t__builder.SetException(exception);
            return;
        
        <>1__state = -2;
        <>t__builder.SetResult(result);
    

    void IAsyncStateMachine.MoveNext()
    
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    
        <>t__builder.SetStateMachine(stateMachine);
    

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    

现在对比非async passthru 编译成的内容:

private Task<string> PassthroughAsync(int x)

    return DoSomethingPrettyAsync(x);

除了绕过大量的struct 初始化和方法调用之外,如果它实际上是异步的,则可能会在堆上“装箱”(在已经同步完成的情况下它不会“装箱”), PassthroughAsync 也将是 JIT 内联的绝佳候选者,因此在实际 CPU 操作码中,PassthroughAsync 可能甚至不存在。

【讨论】:

以上是关于在异步方法中省略异步和等待[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在.NET中调用异步函数而不等待它[重复]

等待先前的异步功能完成[重复]

在for-each-loop中等待异步调用[重复]

在同步方法中执行异步方法的困惑[重复]

如何在c#中正确实现等待异步[重复]

回调异步等待[重复]