当不涉及真正的 I/O 调用时,异步链的所有级别是否需要配置等待(false)



在 Azure 文档 Db 客户端 SDK 之上实现可重用的适配器类型的库。

该库可以在任何地方运行,不仅在 ASP.NET Core Web服务中,而且在命令行应用程序,ASP.NET Web API等中。

在此库中,所有方法都是异步的,它们只是抽象层,可以更轻松地使用文档 Db 客户端 api。唯一真正的异步调用 - I/O 请求 - 实际上是由文档 Db SDK 中的 API 在最低层完成的。我上面写的任何代码都只是在内存数据转换、转换中,不涉及实际的 I/O 调用,但它们也是异步的,因为最低层的文档 Db API 是异步的。

我是否仍然需要在堆栈中的所有上层代码上使用ConfigureAwait(false),或者这是否足以仅在我自己的代码的最低层上调用ConfigureAwait(False),该代码调用进行实际 I/O 调用的文档 Db SDK 的方法?

await调用之前的同步代码不同,延续在不同的调用堆栈调用的上下文中执行。特别是,它们被安排为作为单独的委托执行,并且它们之间没有调用堆栈关系。因此,为最后一个延续执行指定ConfigureAwait(false)将仅对此特定延续生效,其他延续仍将使用其单独的调度配置执行。也就是说,如果您的目标是确保不会通过库中的任何延续来捕获同步上下文(以防止潜在的死锁或任何其他原因(,则应配置所有与 ConfigureAwait(false) 具有延续await调用。

ConfigureAwait(false) 用于防止在初始SynchronizationContext执行。例如,如果您正在使用不需要访问 UI 线程的库(在 WPF 或 WinForms 的情况下(,则应在所有级别上使用ConfigureAwait(false)。否则SynchronizationContext将被恢复。下面是一个简单的WinForms应用程序的示例:

public partial class Form1 : Form
{
    static readonly HttpClient _hcli = new HttpClient();
    public Form1()
    {
        InitializeComponent();
    }
    private static string log;
    private async void button1_Click(object sender, EventArgs e)
    {
        log = "";
        await M3();
        MessageBox.Show(log);
    }
    static async Task<string> M3()
    {
        LogBefore(nameof(M3));
        var str = await M2();
        LogAfter(nameof(M3));
        return str;
    }
    static async Task<string> M2()
    {
        LogBefore(nameof(M2));
        var str = await M1();
        LogAfter(nameof(M2));
        return str;
    }
    static async Task<string> M1()
    {
        LogBefore(nameof(M1));
        var str = await _hcli.GetStringAsync("http://mtkachenko.me").ConfigureAwait(false);
        LogAfter(nameof(M1));
        return str;
    }
    static void LogBefore(string method)
    {
        log += $"before {method} {Thread.CurrentThread.ManagedThreadId} {SynchronizationContext.Current == null}, ";
    }
    static void LogAfter(string method)
    {
        log += $"after {method} {Thread.CurrentThread.ManagedThreadId} {SynchronizationContext.Current == null}, ";
    }
}

输出:

before M3 1 False
before M2 1 False
before M1 1 False
after M1 12 True //sync.context skipped because of .ConfigureAwait(false)
after M2 1 False //sync.context restored
after M3 1 False //sync.context restored

相关内容

  • 没有找到相关文章

最新更新