假设我有以下代码(仅用于学习目的):
static async Task Main(string[] args)
{
var results = new ConcurrentDictionary<string, int>();
var tasks = Enumerable.Range(0, 100).Select(async index =>
{
var res = await DoAsyncJob(index);
results.TryAdd(index.ToString(), res);
});
await Task.WhenAll(tasks);
Console.WriteLine($"Items in dictionary {results.Count}");
}
static async Task<int> DoAsyncJob(int i)
{
// simulate some I/O bound operation
await Task.Delay(100);
return i * 10;
}
我想知道如果我这样做会有什么不同:
var tasks = Enumerable.Range(0, 100)
.Select(index => Task.Run(async () =>
{
var res = await DoAsyncJob(index);
results.TryAdd(index.ToString(), res);
}));
我在这两种情况下得到了相同的结果。但是代码执行起来是否相似?
最佳答案
Task.Run
用于在线程池线程中执行受 CPU 限制的同步操作。由于您正在运行的操作已经是异步的,因此使用 Task.Run
意味着您正在安排工作在线程池线程中运行,而该工作只是开始 一个异步操作,然后几乎立即完成,并在不阻塞该线程池线程的情况下继续执行它必须执行的任何异步工作。因此,通过使用 Task.Run
,您正在等待线程池中的调度工作,但实际上并没有执行任何有意义的工作。您最好只在当前线程中启动异步操作。
唯一的异常(exception)是如果 DoAsyncJob
实现不当并且由于某种原因实际上不是异步的,与其名称和签名相反,并且实际上在返回之前做了很多同步工作。但如果它这样做了,你应该只修复那个有问题的方法,而不是使用 Task.Run
来调用它。
附带说明一下,没有理由让 ConcurrentDictionary
在这里收集结果。 Task.WhenAll
返回您已执行的所有任务的结果集合。就用那个。现在您甚至不需要一个方法来包装您的异步方法并以任何特殊方式处理结果,进一步简化了代码:
var tasks = Enumerable.Range(0, 100).Select(DoAsyncJob);
var results = await Task.WhenAll(tasks);
Console.WriteLine($"Items in results {results.Count}");
关于c# - 在 Select LINQ 方法中使用 Task.Run(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53499688/