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

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

我在这两种情况下得到了相同的结果。但是代码执行起来是否相似?

最佳答案

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/

相关文章:

rust - 如何通过特征委派具有非静态参数的异步函数?

javascript - 从 selenium webdriver promise 获得最终输出的简洁方法

c#-4.0 - TPL TaskFactory.FromAsync 与具有阻塞方法的任务

浏览器取消请求时出现 ASP.NET Web API OperationCanceledException

c# - 一路async await async

c# - 我的 lambda 表达式有什么问题

c# - 通过 Internet 更新我的 Windows 应用程序的新方法

c# - 使用记事本制作 ASP.NET 应用程序

typescript - 异步/等待清晰度,以 sleep 为例

c# - 如何一劳永逸地停止 Entity Framework 6 中的迁移?