C# 异步/等待的体系结构

C# 异步/等待的体系结构,c#,.net,asynchronous,async-await,c#-5.0,C#,.net,Asynchronous,Async Await,C# 5.0,如果您在体系结构的较低级别上使用async/await,是否有必要一直“冒泡”异步/await调用,因为您基本上是在为每一层创建一个新线程,所以效率是否很低(异步调用每一层的异步函数,还是这并不重要,只是取决于您的偏好 我正在使用EF6.0-alpha3,这样我就可以在EF中使用异步方法 我的存储库是这样的: public class EntityRepository:i其中E:class { 公共异步虚拟任务保存() { wait context.saveChangesSync(); } }

如果您在体系结构的较低级别上使用async/await,是否有必要一直“冒泡”异步/await调用,因为您基本上是在为每一层创建一个新线程,所以效率是否很低(异步调用每一层的异步函数,还是这并不重要,只是取决于您的偏好

我正在使用EF6.0-alpha3,这样我就可以在EF中使用异步方法

我的存储库是这样的:

public class EntityRepository:i其中E:class
{
公共异步虚拟任务保存()
{
wait context.saveChangesSync();
}
}
现在,我的业务层如下所示:

public抽象类ApplicationBCBase:IEntityBC
{
公共异步虚拟任务保存()
{
等待repository.Save();
}
}
当然,我的UI中的方法在调用时也会遵循相同的模式

这是:

  • 必要的
  • 对业绩不利
  • 只是偏好的问题
  • 即使这没有在单独的层/项目中使用,如果我在同一类中调用嵌套方法,同样的问题也适用:

    private异步任务Dosomething1()
    {
    //其他东西
    ...
    return wait Dosomething2();
    }
    专用异步任务Dosomething2()
    {
    //其他东西
    ...
    return wait Dosomething3();
    }
    专用异步任务Dosomething3()
    {
    //其他东西
    ...
    返回等待任务。运行(()=>“”);
    }
    
    由于您基本上是为每一层创建一个新线程(异步地为每一层调用一个异步函数),这是不是效率低下,或者这并不重要,只是取决于您的偏好

    不需要。异步方法不一定使用新线程。在这种情况下,由于底层异步方法调用是IO绑定的方法,因此实际上不应该创建新线程

    这是:

    如果要使操作保持异步,就必须“冒泡”异步调用。不过,这确实是首选,因为它允许您充分利用异步方法,包括在整个堆栈中将它们组合在一起

    2. negative on performance
    
    不。正如我提到的,这不会创建新线程。这会带来一些开销,但大部分开销可以最小化(见下文)

    如果您想保持异步,则不需要这样做。您需要这样做以保持整个堆栈的异步

    现在,您可以做一些事情来提高性能。如果您只是包装一个异步方法,您不需要使用语言功能-只需返回
    任务

    public virtual Task Save()
    {
        return repository.Save();
    }
    
    repository.Save()
    方法已经返回了一个
    任务
    ——您不需要等待它,只需将其包装回
    任务
    。这将使该方法的效率有所提高

    您还可以使用“低级”异步方法来防止它们需要调用同步上下文:

    private async Task<string> Dosomething2()
    {
        //other stuff 
        ...
        return await Dosomething3().ConfigureAwait(false);
    }
    
    如果您正在创建一个异步方法,该方法在内部使用
    Task.Run
    围绕一些本身不异步的内容“创建异步”,那么您实际上是在将同步代码包装到异步方法中。这将使用线程池线程,但可以“隐藏”它这样做的事实,有效地使API产生误导。通常最好将调用留给
    任务。为最高级别的调用运行
    ,并让底层方法保持同步,除非它们真正能够利用异步IO或
    任务以外的某种卸载方式。运行
    。(这并不总是正确的,但通过
    Task.Run
    、然后通过async/await返回的同步代码中的“异步”代码通常是有缺陷的设计的标志。)

    如果您在体系结构的较低级别上使用async/await,是否有必要一直“冒泡”异步/await调用,因为您基本上是在为每一层创建一个新线程,所以效率是否很低(异步调用每一层的异步函数,还是这并不重要,只是取决于您的偏好

    这个问题暗示了一些误解

    首先,不要每次调用异步函数时都创建一个新线程

    其次,您不需要声明异步方法,因为您正在调用异步函数。如果您对已返回的任务感到满意,只需从没有异步修饰符的方法返回:

    public class EntityRepository<E> : IRepository<E> where E : class
    {
        public virtual Task Save()
        {
            return context.SaveChangesAsync();
        }
    }
    
    public abstract class ApplicationBCBase<E> : IEntityBC<E>
    {
        public virtual Task Save()
        {
            return repository.Save();
        }
    }
    
    最好写为:

    public Task<string> Foo()
    {
        var bar = new Bar();
        bar.Baz();
        return bar.Quux();
    }
    
    公共任务Foo() { var bar=新的bar(); bar.Baz(); 返回条.qux(); }

    (从理论上讲,创建的任务和调用方可以添加延续的任务之间存在非常微小的差异,但在绝大多数情况下,您不会注意到任何差异。)

    请注意,您也可以标记一条消息,例如您描述为
    async
    的消息,以添加错误处理。如果您等待任务,您可以将调用包装在
    try/catch
    块中,而不是向提供的任务添加延续。@Servy:是的,但在这种情况下,它不符合不提供额外处理的描述。记住,在不改变API的情况下,这样做很方便……因此,为了理解它,我只需要在同一函数中后面的某个函数需要返回异步方法时使用async/await?如果是fire and forget或last语句,那么就不需要await了?@Valdeteo:在这种情况下,这不是真的lly“发射并忘记”-您仍然返回一个
    任务
    ,该任务可用于查看操作何时完成、失败等。用一句话准确地总结事情是很困难的-最好深入了解等待实际上为您做了什么。@JamesManning:不,它也适用于非异步/等待版本,因为
    private async Task<string> Dosomething3()
    {
        //other stuff 
        ...
        // Potentially a bad idea!
        return await Task.Run(() => "");
    }
    
    public class EntityRepository<E> : IRepository<E> where E : class
    {
        public virtual Task Save()
        {
            return context.SaveChangesAsync();
        }
    }
    
    public abstract class ApplicationBCBase<E> : IEntityBC<E>
    {
        public virtual Task Save()
        {
            return repository.Save();
        }
    }
    
    public async Task<string> Foo()
    {
        var bar = new Bar();
        bar.Baz();
        return await bar.Quux();
    }
    
    public Task<string> Foo()
    {
        var bar = new Bar();
        bar.Baz();
        return bar.Quux();
    }