在 C# 中启动数千个任务的最佳方法是什么

dea*_*dog 5 c# multithreading task-parallel-library

假设我有一个List用户,我想对所有用户执行一些操作,例如更新他们在数据库中的一个属性。用户列表可能包含数万或数十万用户。如果对用户完成的所有工作都在我的机器本地,那么我将只使用Parallel.ForEach它们来处理它们,但是因为这将涉及等待(可能需要很多秒)以完成对外部服务的调用,我认为这是更适合使用Tasks。

现在,这是我拥有的代码:

        Task.WaitAll(usersList.Select(user => Task.Run(() => async
        {
            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                await UpdateUserInExternalService(user);
            }
            catch (Exception ex)
            {
                LogError($"Something went wrong with user 'user.Username'.", ex);
            }
        }, cancellationToken)).ToArray());
Run Code Online (Sandbox Code Playgroud)

我有一些问题:

  1. 如果列表中有 10,000 个用户,它是否真的一次创建了 10,000 个任务(巨大的内存峰值)?或者它只是创建,比如说 10,然后随着一些任务的完成,其他的会被启动?
  2. 我如何使用取消令牌看起来正确吗?
  3. 如果我在调用它后立即取消操作,它是否仍然需要在取消之前启动所有 10,000 个任务,还是因为我将取消标记传递给 Task.Run 参数而避免了这种情况?

在我的测试中,它似乎运行良好。我只是想确保没有我忽略的任何东西或我可能遇到的一些问题,或者是否有我没有遵循的最佳实践。

我愿意接受任何建议。提前致谢。

更新

根据评论,我对外部服务或其数据库没有任何控制权。我只有他们提供给我的电话,这需要一个用户。谢谢。

usr*_*usr 5

是的,这段代码会立即启动所有操作。并Task.Run没有多大作用。由于这是使用异步 IO,因此线程池几乎没有参与。它不会限制这个。

不要这样做。这可能会使某些资源过载。使用https://blogs.msdn.microsoft.com/pfxteam/2012/03/05/implementing-a-simple-foreachasync-part-2/中的最后一段代码。这样的事情应该在框架中,因为几乎总是需要为 IO 工作选择精确的并行度。

(3)修复后就没有关系了。在修复之前,令牌几乎没有影响,因为所有操作都会立即启动,导致令牌仅立即检查一次。此后将无法再取消。

您还可以将令牌传递给UpdateUserInExternalService以实现更立即的取消。