实际的最大并行任务。

发布于 2025-02-03 03:48:38 字数 555 浏览 4 评论 0原文

我希望此代码要执行1秒钟:

public async void Test()
{
    DateTime start = DateTime.Now;
    await Parallel.ForEachAsync(new int[1000], new ParallelOptions { MaxDegreeOfParallelism = 1000 }, async (i, token) =>
    {
        Thread.Sleep(1000);
    });
    Console.WriteLine("End program: " + (DateTime.Now - start).Seconds + " seconds elapsed.");
}

相反,在我的PC上需要37秒(i7-9700 8核8-thread):

End program: 37 seconds elapsed.

我正在使用MaxDegreeofParallelism = 1000生成1000个任务。 ...为什么他们都不同时运行?

I would expect this code to take 1 second to execute:

public async void Test()
{
    DateTime start = DateTime.Now;
    await Parallel.ForEachAsync(new int[1000], new ParallelOptions { MaxDegreeOfParallelism = 1000 }, async (i, token) =>
    {
        Thread.Sleep(1000);
    });
    Console.WriteLine("End program: " + (DateTime.Now - start).Seconds + " seconds elapsed.");
}

Instead, it takes 37 seconds on my pc (i7-9700 8-core 8-thread):

End program: 37 seconds elapsed.

I am generating 1000 tasks with MaxDegreeOfParallelism = 1000....why don't they all run simultaneously?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我早已燃尽 2025-02-10 03:48:38

方法调用异步body threadpool 线程。通常,此委托会迅速返回valuettask,但是在您的情况下,这不是发生的情况,因为您的代表并不是真正的异步:

async (i, token) => Thread.Sleep(1000);

您可能会在这里发出编译器警告,关于async 缺少等待运算符的方法。但是,将混合的同步/异步工作负载给并行。foreachAsync方法还可以。此方法旨在处理任何类型的工作量。但是,如果工作负载主要是同步的,则可能是饱和的threadpool

threadpool已经创建了 emovention.processorcount ,并且需要做更多的工作。在这种情况下,threadpool切换到保守的算法,该算法每秒都会创建一个新线程(截至.NET 6)。这种行为不会精确记录,并且可能会在未来的.NET版本中改变。

为了获得所需的行为,即在所有1000个输入中运行委托,您必须增加threadpool立即根据需求创建的线程数:

ThreadPool.SetMinThreads(1000, 1000); // At the start of the program

有些人会这样做之后,您将不再有一个线程池,因为一个线程池意味着是一小池可重复使用的线程。但是,如果您不在乎语义,而只想完成工作,那么无论后果如何-c-sharp-extly-1-mb“>内存消耗在操作系统级别上的开销,这是解决问题的最简单方法。

The Parallel.ForEachAsync method invokes the asynchronous body delegate on ThreadPool threads. Usually this delegate returns a ValueTask quickly, but in your case this is not what happens, because your delegate is not really asynchronous:

async (i, token) => Thread.Sleep(1000);

You are probably getting here a compiler warning, about an async method lacking an await operator. Nevertheless giving a mixed sync/async workload to the Parallel.ForEachAsync method is OK. This method is designed to handle any kind of workload. But if the workload is mostly synchronous, the result might be a saturated ThreadPool.

The ThreadPool is said to be saturated when it has already created the number of threads specified by the SetMinThreads method, which by default is equal to Environment.ProcessorCount, and there is more demand for work to be done. In this case the ThreadPool switches to a conservative algorithm that creates one new thread every second (as of .NET 6). This behavior is not documented precisely, and might change in future .NET versions.

In order to get the behavior that you want, which is to run the delegate for all 1000 inputs in parallel, you'll have to increase the number of threads that the ThreadPool creates instantly on demand:

ThreadPool.SetMinThreads(1000, 1000); // At the start of the program

Some would say that after doing so you won't have a thread pool any more, since a thread pool is meant to be a small pool of reusable threads. But if you don't care about the semantics and just want to get the job done, whatever the consequences are regarding memory consumption and overhead at the operating system level, that's the easiest way to solve your problem.

生生漫 2025-02-10 03:48:38

我不知道foreachAsync的确切实现,但我假设他们使用task s,而不是thread s。

当您使用1000 任务 s运行1000 CPU绑定操作时,实际上并未创建1000 thread s,您只是在问一些thread> threadpool 线程 s运行这些操作。这些线程 s被Sleep调用阻止,因此大多数任务 s在开始执行之前被排队。

这就是为什么在任务中或一般的异步上下文中调用thread.sleep是一个可怕的想法的原因。如果您编辑代码以异步而不是同步等待,那么经过的时间可能会更接近一秒钟。

await Parallel.ForEachAsync(new int[1000], new ParallelOptions { MaxDegreeOfParallelism = 1000 }, async (i, token) =>
{
    await Task.Delay(1000);
});

I do not know the exact implementation of ForEachAsync, but I assume that they use Tasks, not Threads.

When you use 1000 Tasks to run 1000 CPU bound operations, you are not actually creating 1000 Threads, you are just asking a handful of ThreadPool Threads to run those operations. Those Threads are blocked by the Sleep calls, so most of the Tasks are queued up before they can start execution.

This is exactly why it is a horrible idea to call Thread.Sleep in a Task, or in async contexts in general. If you edit your code to wait asynchronously instead of synchronously, the time elapsed will probably be much closer to a second.

await Parallel.ForEachAsync(new int[1000], new ParallelOptions { MaxDegreeOfParallelism = 1000 }, async (i, token) =>
{
    await Task.Delay(1000);
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文