Select LINQ 操作中的异步方法未按预期并行执行

问题描述 投票:0回答:2

让我们考虑一下这段代码:

var tasks = actionItems.Select(t => DoSmthAsync());
var resultAll = await Task.WhenAll(tasks);

及方法:

public async Task<int> DoSmthAsync()
{
    Debug.WriteLine("Threadid=" + Thread.CurrentThread.ManagedThreadId);
    var result = await DoSmthAnotherAsync();
    Debug.WriteLine("ThreadId=" + Thread.CurrentThread.ManagedThreadId);
    return result;
}

我希望这将在不同的线程中并行执行,但我看到它在同一个线程中工作。另外,我不明白任务何时运行?

c# linq asynchronous parallel-processing async-await
2个回答
3
投票

您将

async await
模型与并行编程混淆了。

Select
将连续迭代你的
actionItems
。当您等待
DoSmthAsync
时,
Select
会将控制权返回给调用者(在本例中为
DoSmthAnotherAsync
迭代器),因此您将获得在一个线程上创建的
Task<int>
列表。

要并行运行

Select
语句,您需要使用 PLINQ

var tasks = actionItems.AsParallel().Select(t => DoSmthAsync());

这将产生您预期的结果。

然后您需要

await Task.WhenAll(tasks);
等待任务结果。然后,您可以访问
Task<int>
列表中每个
tasks
的结果及其各自的
Result
属性。


2
投票

我希望这将在不同的线程中并行执行。

这在Victor Wilson的回答中进行了解释,所以我将重点解释这一点:

另外,我不明白任务何时运行?

A

Task
通常在创建时启动(在这种情况下,它称为 hot 任务)。异步方法和所有内置 .NET API 都会返回热门任务。可以使用
Task
构造函数
(创建所谓的 cold 任务)创建未启动的 Task,但这些任务通常由创建它们的同一个库在内部启动。向外部世界暴露一个冷酷的任务,结束期望调用者
Start
它,是我从未见过的事情,如果存在的话,那将是非常令人惊讶的(以一种不愉快的方式)。

此构造函数仅适用于需要将任务的创建和启动分开的高级场景。

var tasks = actionItems.Select(t => DoSmthAsync());

这行代码没有创建任何任务,因此此时没有启动任何任务。

Select
LINQ 方法返回一个延迟可枚举序列,而不是物化集合。当您调用方法
Task.WhenAll(tasks)
时,任务的实际实现就会发生。

© www.soinside.com 2019 - 2024. All rights reserved.