在异步编程模型中编写代码的主要目的之一(更具体地说 - 使用回调而不是阻塞线程)是为了最小化系统中阻塞线程的数量。
对于运行线程,由于上下文切换和同步成本,这个目标是显而易见的。
但是被阻止的线程呢?为什么减少它们的数量如此重要?
例如,在等待来自Web服务器的响应时,线程被阻塞,并且不占用任何CPU时间,并且不参与任何上下文切换。
所以我的问题是:除了RAM(每个线程大约1MB?)阻塞线程占用了哪些其他资源?
另一个更主观的问题是:在什么情况下,这个成本真的证明了编写异步代码的麻烦(例如,价格可能是将你的好的连贯方法拆分为大量的beginXXX和EndXXX方法,并将参数和局部变量移动到类字段)。
更新 - 我没有提及或没有给予足够重视的其他原因:
所以我的问题是:除了RAM(每个线程大约1MB?)阻塞线程占用了哪些其他资源?
这是最大的一个。话虽这么说,有一个原因是.NET中的ThreadPool允许每个核心有这么多线程 - 在3.5 the default was 250 worker threads per core in the system中。 (在.NET 4中,它依赖于系统信息,如虚拟地址大小,平台等 - 现在没有固定的默认值。)线程,特别是被阻塞的线程,真的不是那么昂贵......
但是,我会说,从代码管理的角度来看,值得减少阻塞线程的数量。每个被阻塞的线程都是一个应该在某个时刻返回并被解除阻塞的操作。拥有许多这些意味着您需要管理相当复杂的代码集。保持这个数字减少将有助于保持代码库更简单 - 更易于维护。
另一个更主观的问题是:在什么情况下,这个成本真的证明了编写异步代码的麻烦(例如,价格可能是将你的好的连贯方法拆分为大量的beginXXX和EndXXX方法,并将参数和局部变量移动到类字段)。
现在,这通常很痛苦。这很大程度上取决于场景。然而,.NET 4中的Task<T>
类在很多情况下都会对此进行改进。使用TPL,它比以前使用APM(BeginXXX / EndXXX)甚至EAP要痛苦得多。
这就是语言设计师为improving this situation in the future投入如此多精力的原因。他们的目标是使异步代码更容易编写,以便更频繁地使用它。
除了阻塞线程可能持有锁的任何资源之外,还要考虑线程池大小。如果你已达到最大线程池大小(如果我没记错,对于.NET 4,最大线程数是每个CPU 100个),你将无法在线程池上运行任何其他东西,直到至少有一个线程获得释放。
我想指出堆栈内存的1MB数字(或256KB,或其设置的任何内容)是一个储备;虽然它确实占用了可用的地址空间,但实际的内存只能在需要时提交。
另一方面,拥有非常多的线程必然会使任务调度程序陷入困境,因为它必须跟踪它们(自上次打勾以来已经变为可运行,依此类推)。