如果你开始太multithreading会发生什么?

当你创build太多的线程会发生什么? 它是否会导致CPU崩溃或在Windows操作系统上有某种内部负载均衡机制?

我正在运行以下代码:

private async void A(string[] a) { var tasks = a.Select(B); await Task.WhenAll(tasks); } private async Task B(string b) { new Thread(async delegate () { //all the work that needs to be done }).Start(); } 

我正在运行一个asynchronous任务数组,但在每个asynchronous方法中,我已经封装了所有需要在新线程中完成的工作。 如果我打了很多次电话会发生什么? 处理器如何处理太多的线程?

首先,为什么从任务内运行线程? 在99.9%的情况下,这是没有意义的。 在剩余的0.1%中,maaybe有一点意义,但是你最有可能应该使用TaskCompletionSource而不是Task。

任务的设计是为了让你可以有调度程序来排队这些任务,监视这些任务的睡眠/等待/等等,并重新使用线程同时运行其他任务。

基本上,你将“工作”包装到任务中,然后将这些任务交给调度程序,然后调度程序决定是否,何时以及要运行多少个线程来执行这些任务。

调度员并不是魔术,他们没有一个水晶球来预测未来。 我说他们是“决定”的,但这只是一半的事实:调度人员通常遵循一些通用的规则,具体取决于其类型。 所以,你选择正确的调度程序为你的幻想和完成。

认真地说,放弃目前的做法。 改用调度器。 你甚至可以有一个调度程序,将执行每个任务在单独的线程。 这将相当于你目前的做法。 但是,那么您将能够快速切换到另一个调度程序,并感受到不同之处。

这里有几个资源给你,一个非常重要的库:

  • 许多其他类型的调度程序开箱即用
  • 进入提供它们的图书馆
  • 与lib的nuget

认真。 如果你不想读/ etc,那么只要读取第一篇文章,然后阅读不同调度器的名字 ,至少可以知道你选择忽略了多少种可能性。

最后,回答这个问题,Windows是有点负载平衡的。 它会尽量防止运行太多的线程。 在给定的时间点,它实际上会运行少量的线程(或多或少等于处理器中逻辑执行单元的数量),其余的将会休眠并等待其时间。 Windows会在他们之间切换,所以你会看到,如果他们都在运行,但其中一些较慢,其中一些更快。

但是,这并不意味着您可以创建无限量的线程。 显然,有一个内存限制:如果你有X GB的内存,你不能保留更多的内存。 我现在开玩笑,但是由于有一些明显的限制,会有更多的限制。 不过,这里有一点严肃,因为你看,每个线程都有一个STACK,这个栈可以是兆字节,所以如果你有32位处理器,STACK的数量最多可以达到几千。 所以..是的,记忆可以是一个限制。 这在64位上不太明显,但是,当然,你没有足够的RAM来填满整个64位地址空间,所以在64位上你也会有一个限制。

由于Windows将尝试保留所有线程的记录,即使是那些睡眠的记录,也会浪费时间跟踪这些记录。 而且,这会浪费时间进行切换,因为作为一个操作系统,它会尽量保持它们的切换和运行。 它直接意味着你创建的线程越多(1/10/100/1000 / ..),所有的东西都会运行得更慢 – 而且比N线程分割的速度要慢(不是:1 / 0.1 / 0.01 / 0.001 / .. :1 / 0.1 / 0.097 / 0.0089 / ..),因为在记录和切换时浪费了时间。

线程也有优先权。 内部系统线程通常具有更高的优先级 系统会比你更频繁地切换到它们,这意味着你运行的线程越多,甚至应用程序处理的速度越慢。

也有一个硬限制。 为了跟踪重要的对象,Windows使用“句柄”的概念。 每个窗口,每个线程,每个共享内存块,每个打开的文件流等,只要它活着(和更长一点) – 具有唯一的句柄。 你可以通过使用所有的句柄实际上STARVE窗口。

例如,如果你用完所有的GUI句柄,你将无法打开新的窗口。 或窗口区域。 或控制。 想象一下,打开一个记事本,它将启动并显示没有菜单,也没有文本区域,因为没有足够的空闲句柄来分配它们。

由于这个限制,Windows实际上限制了每个进程分配的句柄的数量。 这意味着,例如,Windows有一个1M的句柄池,但每个进程只能使用高达1K的句柄。 这些数字是人为的,只是让你明白。

由于物理(本地)线程必须有句柄,这是另一个限制。

在这件事上,我不是一个真正的专家,让我们回到专家写的一系列文章,他们转变了线程限制,处理限制等等:

https://blogs.technet.microsoft.com/markrussinovich/2009/07/05/pushing-the-limits-of-windows-processes-and-threads/

CPU只执行操作系统告诉它的内容,操作系统负责运行哪些线程,在中断之前运行多长时间。 在调度程序中内置了一些反饥饿功能,所以它不应该完全锁定系统,但是如果在内存或地址空间不足的情况下只保留尽可能多的线程,那么您几乎可以将其瘫痪。

如果我们假装你的程序是唯一运行的程序,那么如果任务是CPU限制的话,理想的线程数与CPU核心数是一样的。 如果任务受I / O限制或需要在内核对象上等待,那么更多的线程可能是理想的。

如果您创建了数千个线程,那么您将浪费时间在它们之间进行上下文切换,而您的工作将需要更长时间才能完成 而不是手动启动新线程,您应该使用线程池来执行您的工作,以便Windows本身可以平衡最佳线程数。

await和其他高级异步关键字可能已经使用线程池。