我正在试图理解在async foreach循环中设置ConfigureAwait(false)的影响?

await foreach (var item in data.ConfigureAwait(false))
{
  //...
}

我们到底在哪里丢失了同步上下文?我什么时候想要在上面的foreach外观内设置ConfigureAwait(false)

我理解在异步数据源中这样做的意义,但是foreach循环又如何呢?

我希望循环中的上下文不会有同步上下文,但我的实验表明上下文仍然存在.

以下是我的示例Winform应用程序:

private async void button1_Click(object sender, EventArgs e)
{
    var dataSource = new DataFactory();

    IAsyncEnumerable<int> data = dataSource.GetData();

    await foreach (var item in data.ConfigureAwait(false))
    {
        Console.WriteLine(item);
        DataFactory.PrintContext("Data - item: " + item);
    }
}

public class DataFactory
{
    public async IAsyncEnumerable<int> GetData()
    {
        for (int i = 0; i < 10; i++)
        {
            await Task.Delay(250).ConfigureAwait(true);
            PrintContext("Generating Data: " + i);
            yield return i;
        }
    }

    public static void PrintContext(string message)
    {
        Debug.WriteLine($"{message}, TID={Environment.CurrentManagedThreadId}, SyncContext.Current={(SynchronizationContext.Current?.GetType().Name?.ToString() ?? "null")}");
    }
}

运行代码时的样例输出:

Generating Data: 0, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Data - item: 0, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Generating Data: 1, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Data - item: 1, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Generating Data: 2, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Data - item: 2, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Generating Data: 3, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext
Data - item: 3, TID=1, SyncContext.Current=WindowsFormsSynchronizationContext

推荐答案

GetData迭代器捕获每个await上的环境同步上下文,因为await配置了ConfigureAwait(true).因此,await之后的继续在捕获的同步上下文上运行.因为GetData是在UI线程上枚举的,所以环境同步上下文是WindowsFormsSynchronizationContext,它被捕获,所以继续在UI线程上运行.您认为在枚举上使用外部ConfigureAwait(false)会以某种方式将枚举重定向到ThreadPool的预期是没有根据的.ConfigureAwait仅配置上下文的捕获.它不是重定向器/上下文切换器.如果要在ThreadPool上运行枚举,只需将整个枚举括在Task.Run中:

await Task.Run(async () =>
{
    await foreach (var item in data)
    {
        Console.WriteLine(item);
        DataFactory.PrintContext("Data - item: " + item);
    }
}

这样,GetData迭代器将找不到要捕获的同步上下文,使得内部ConfigureAwait(true)无关紧要.枚举将从ThreadPool开始(因为Task.Run),并将停留在ThreadPool上,因为这是Task.Delay被设计完成的地方.所以这是巧合.Task.Run本身并不能保证整个枚举都发生在ThreadPool上.它只影响枚举开始的位置.

Csharp相关问答推荐

如何打印已添加到List的Linq值,而不是C#中的:System.Collections.Generic.List ' 1[System.Int32]?

如何使用Automapper映射两个嵌套列表

C#将参数传递给具有变化引用的变量

使用特定格式的JsonConvert序列化对象

为什么Blazor值在更改后没有立即呈现?

使用LayoutKind在C#中嵌套 struct .显式

在发布表单时绑定包含附加(嵌套)列表的对象列表的正确语法是什么

Polly v8—使用PredicateBuilder重试特定的状态代码

选取器与.NET Maui MVVM的绑定属性

有没有办法在WPF文本框中添加复制事件的处理程序?

C#阻塞调用或await calling inside calling方法

net中从位图获取坐标和绘制折线

具有以接口为其类型的属性的接口;类指定接口的实现,但无效

如何在我的C#应用程序中设置带有reactjs前端的SignalR服务器?

单元测试类型为HttpClient with Microsoft.Extensions.Http.Resilience

Visual Studio如何使用当前的框架?

如何使用IHostedService添加数据种子方法

当我在Git中暂存文件更改时,它们会消失

Unity 3D-意外轴捕捉和未知力对脉冲react 行为的影响

是否在异步方法中避免Span<;T>;.ToArray()?