【问题标题】:Microsoft.AspNetCore.Identity UserManager GetUserAsyncMicrosoft.AspNetCore.Identity UserManager GetUserAsync
【发布时间】:2016-12-14 04:49:01
【问题描述】:

我一直在阅读 UserManager.cs 的源代码,我已经进入了以下块:

        public virtual Task<TUser> GetUserAsync(ClaimsPrincipal principal)
    {
        if (principal == null)
        {
            throw new ArgumentNullException(nameof(principal));
        }
        var id = GetUserId(principal);
        return id == null ? Task.FromResult<TUser>(null) : FindByIdAsync(id);
    }

我一直很好奇上面的内容不是这样的原因:

        public virtual async Task<TUser> GetUserAsync(ClaimsPrincipal principal)
    {
        if (principal == null)
        {
            throw new ArgumentNullException(nameof(principal));
        }
        var id = GetUserId(principal);
        return id == null ? await Task.FromResult<TUser>(null) : await FindByIdAsync(id);
    }

如你所见,我只添加了 async/await。

*我正在用另一个 ORM 实现 UserManager 类。

【问题讨论】:

    标签: asp.net async-await task-parallel-library asp.net-identity


    【解决方案1】:

    由于不是我写的,我无法确定。 ;) 但我想这与return await-anti 模式有关。

    在 GitHub 上甚至有一个问题,要求 Roslyn 在编译时处理它。

    Optimize an async method that ends "return await e" to be non-async "return e"

    这种优化将提高异步方法的性能 其唯一的等待出现在最后。不是在生成调试代码和 不是嵌套在 try 块中。

    引用他在this question上发布的damien_the_unbeliever的精彩评论

    await 是一种暂停当前方法直到其他一些 代码已经完成了它的工作。如果你的方法要做的所有事情 它的恢复是说“我完成了”那你为什么要去所有的 努力?话虽如此,实际的反模式是return await if 这是方法中的 only await(因为它在这里),它不是 在带有finallyusing 块的try 块内(在这种情况下 返回后还有额外的代码要运行

    使用await/async 时,应该注意它所产生的开销。

    以这两个类为例:

    public class BaseClass
    {
        public async Task<int> GetIdAsync()
        {
            return await Task.Run(() => 100);
        }
    }
    
    public class Foo : BaseClass
    {
        public Task<int> GetId()
        {
            return GetIdAsync();
        }
    }
    
    
    public class Bar : BaseClass
    {
        public async Task<int> GetId()
        {
            return await GetIdAsync();
        }
    }
    

    如果您使用ILSpy 反编译来自Bar 的代码将如下所示:

    public class Bar : BaseClass
    {
        [DebuggerStepThrough, AsyncStateMachine(typeof(Bar.<GetId>d__0))]
        public Task<int> GetId()
        {
            Bar.<GetId>d__0 <GetId>d__ = new Bar.<GetId>d__0();
            <GetId>d__.<>4__this = this;
            <GetId>d__.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
            <GetId>d__.<>1__state = -1;
            AsyncTaskMethodBuilder<int> <>t__builder = <GetId>d__.<>t__builder;
            <>t__builder.Start<Bar.<GetId>d__0>(ref <GetId>d__);
            return <GetId>d__.<>t__builder.Task;
        }
    }
    

    虽然Foo 看起来像这样:

    public class Foo : BaseClass
    {
        public Task<int> GetId()
        {
            return base.GetIdAsync();
        }
    }
    

    所以,总而言之,如果你只是要从一个Task 返回值,那么真的没有理由去await 它。相反,您应该返回 Task 并让 调用者 await 改为 Task。等待Task 会扩展堆栈并产生只会对性能产生负面影响的开销。

    【讨论】:

    • 那么异常传播呢。 Async/await 确保继续并允许正确返回异常。如果我只是任务并且抛出异常,它会产生问题吗?如果 async/await 可以跳过初始线程并继续另一个线程,我尝试找到一个快速答案,但我还没有澄清这一点。
    • GitHub 链接讨论了异常的处理。返回任务确实会改变异常的堆栈。 Task 仍必须等待,因此仍应返回异常,但可能在另一个堆栈中。
    猜你喜欢
    • 2023-04-04
    • 2021-10-22
    • 2017-10-02
    • 2018-08-20
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多