在Select LINQ方法中使用Task.Run()

时间:2018-11-27 12:26:41

标签: c# async-await task-parallel-library

假设我有以下代码(仅出于学习目的):

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);
    }));

在两种情况下我都得到相同的结果。但是代码执行类似吗?

2 个答案:

答案 0 :(得分:1)

Task.Run可以在线程池线程中执行CPU绑定的同步操作。因为您正在运行的操作已经是异步的,所以使用Task.Run意味着您正在调度要在线程池线程中运行的工作,而该工作仅仅是启动异步操作,然后几乎立即完成,并且在不阻塞该线程池线程的情况下执行其必须执行的所有异步工作。因此,通过使用Task.Run,您正在等待调度线程池中的工作,但实际上并没有任何有意义的工作。最好只在当前线程中启动异步操作。

唯一的例外是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}");

答案 1 :(得分:-2)

是的,两种情况的执行方式相似。实际上,它们以完全相同的方式执行。