Lor*_*eno 3 c# linq multithreading asynchronous async-await
我有以下代码:
var things = await GetDataFromApi(cancellationToken);
var builder = new StringBuilder(JsonSerializer.Serialize(things));
await things
.GroupBy(x => x.Category)
.ToAsyncEnumerable()
.SelectManyAwaitWithCancellation(async (category, ct) =>
{
var thingsWithColors = await _colorsApiClient.GetColorsFor(category.Select(thing => thing.Name).ToList(), ct);
return category
.Select(thing => ChooseBestColor(thingsWithColors))
.ToAsyncEnumerable();
})
.ForEachAsync(thingAndColor =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // prints different IDs
builder.Replace(thingAndColor.Thing, $"{thingAndColor.Color} {thingAndColor.Thing}");
}, cancellationToken);
Run Code Online (Sandbox Code Playgroud)
它使用System.Linq.Async并且我发现很难理解。ToList()在“经典”/同步 LINQ 中,只有当我调用或ToArray()使用它时,整个事情才会执行。在上面的示例中,没有此类调用,但 lambda 无论如何都会执行。它是如何工作的?
我的另一个担忧是关于多线程。我多次听说异步!=多线程。那么,怎么可能打印Console.WriteLine(Thread.CurrentThread.ManagedThreadId);出各种ID呢?有些 ID 会被打印多次,但输出中总共有大约 5 个线程 ID。我的代码都没有显式创建任何线程。这都是异步等待。不StringBuilder支持多线程,我想了解上面的实现是否有效。
请忽略我代码的算法,这并不重要,这只是一个例子。重要的是 System.Async.Linq 的使用。
ForEachAsync将具有与 ToList/ToArray 类似的效果,因为它强制评估整个列表。
默认情况下, an 之后的任何内容await都会在同一执行上下文上继续,这意味着如果代码在 UI 线程上运行,它将继续在 UI 线程上运行。如果它在后台线程上运行,它将继续在后台线程上运行,但不一定是同一个后台线程。
但是,您的任何代码都不应该并行运行。这并不一定意味着它是线程安全的,可能需要一些内存屏障来确保正确刷新数据,但我假设这些屏障是由框架代码本身发出的。