首页 > 解决方案 > 我应该在存储库之类的地方使用 ConfigureAwait(false) 吗?

问题描述

刚刚阅读了这篇关于 ConfigureAwait 的文章,它让我想到了一个我已经有一段时间无法平静下来的问题。

考虑下面的代码。每个依赖项都用于await使调用异步。我担心的是,每次我们退出时await它都会返回 UI 线程,我不希望这样,直到我实际上处于需要更新 UI 以减少线程上下文切换的顶层。这让我认为ConfigureAwait(false)应该在 UI 下方的层中使用,以避免不必要Post的 's ( SynchronizationContext) 到 UI 线程。

你怎么看?这是必要的还是我已经走了?或者运行时会真正为我处理这个问题吗?

没有ConfigureAwait(false)

public class ViewModel
{
    private readonly Service service;
    private readonly ICommand updateCommand;
    
    public ViewModel(Service service)
    {
        this.service = service;
        updateCommand = new RelayCommand(UpdateUser);
    }

    private async void UpdateUser()
    {
        Cursor.ShowWait();
        await service.UpdateUser(SelectedUser);
        Cursor.ShowDefault();
    }
}

public class Service
{
    private readonly Repository repository;
    
    public Service(Repository repository)
    {
        this.repository = repository;
    }
    
    public async Task UpdateUser(Model.User user)
    {
        var domainUser = Convert(user);
        await repository.UpdateUser(domainUser);
    }
}

public class Repository
{
    private readonly MyDbContext context;
    
    public Repository(MyDbContext context)
    {
        this.context = context;
    }
    
    public async Task UpdateUser(User user)
    {
        context.Users.Update(user);
        await context.SaveChangesAsync();
    }
}

public class MyDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
}   

使用 ConfigureAwait(false)

public class ViewModel
{
    private readonly Service service;
    private readonly ICommand updateCommand;
    
    public ViewModel(Service service)
    {
        this.service = service;
        updateCommand = new RelayCommand(UpdateUser);
    }

    private async void UpdateUser()
    {
        Cursor.ShowWait();
        await service.UpdateUser(SelectedUser);
        Cursor.ShowDefault();
    }
}

public class Service
{
    private readonly Repository repository;
    
    public Service(Repository repository)
    {
        this.repository = repository;
    }
    
    public async Task UpdateUser(Model.User user)
    {
        var domainUser = Convert(user);
        await repository.UpdateUser(domainUser).ConfigureAwait(false);
    }
}

public class Repository
{
    private readonly MyDbContext context;
    
    public Repository(MyDbContext context)
    {
        this.context = context;
    }
    
    public async Task UpdateUser(User user)
    {
        context.Users.Update(user);
        await context.SaveChangesAsync().ConfigureAwait(false);
    }
}

public class MyDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
}

标签: c#wpfasync-awaittaskconfigureawait

解决方案


您应该始终在库中使用 configureAwait,即使代码(在我看来)比没有代码要糟糕得多。

在此处查看 microsoft 最佳实践

通过使用 ConfigureAwait,您可以启用少量的并行性:一些异步代码可以与 GUI 线程并行运行,而不是不断地用一些工作来纠缠它。

除了性能之外,ConfigureAwait 还有一个重要的方面:它可以避免死锁。

当你不能使用它时要小心:

如果在需要上下文的方法中有 await 之后的代码,则不应使用 ConfigureAwait。对于 GUI 应用程序,这包括任何操作 GUI 元素、写入数据绑定属性或依赖于 GUI 特定类型(例如 Dispatcher/CoreDispatcher)的代码。

对于 ASP.NET 应用程序,这包括使用 HttpContext.Current 或构建 ASP.NET 响应的任何代码

在存储库中,您几乎总是可以使用它,因为您不需要恢复到相同的同步上下文中。


推荐阅读