在异步编程模型中编写代码的主要目的之一(更具体地说 - 使用回调而不是阻塞线程)是最小化系统中阻塞线程的数量。
对于运行线程,由于上下文切换和同步成本,这个目标很明显。
但是被阻止的线程呢?为什么减少他们的数量如此重要?
例如,在等待来自Web服务器的响应时,线程被阻塞,并且不会占用任何CPU时间,也不会参与任何上下文切换。
所以我的问题是: RAM除外(每个线程约1MB?)阻塞线程占用了哪些其他资源?
另一个更主观的问题: 在什么情况下,这个成本真的证明了编写异步代码的麻烦(例如,价格可能是将你的好的连贯方法拆分为大量的beginXXX和EndXXX方法,并将参数和局部变量移动为类字段)。
更新 - 我未提及或未给予足够重视的其他原因:
更多线程意味着更多地锁定公共资源
更多线程意味着更多创建和处理昂贵的线程
系统肯定会耗尽线程/ RAM,然后停止为客户端提供服务(在Web服务器方案中,这实际上可能会导致服务崩溃)
答案 0 :(得分:6)
所以我的问题是:除了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投入大量精力的原因。他们的目标是使异步代码更容易编写,以便更频繁地使用它。
答案 1 :(得分:0)
除了阻塞线程可能持有锁的任何资源外,还需要考虑线程池大小。如果你已达到最大线程池大小(如果我没记错,对于.NET 4,最大线程数是每个CPU 100个),你将无法在线程池上运行任何其他东西,直到至少有一个线程获得释放了。
答案 2 :(得分:0)
我想指出堆栈内存的1MB数字(或256KB,或其设置的任何内容)是一个储备;虽然它确实占用了可用的地址空间,但实际的内存只能在需要时提交。
另一方面,拥有大量线程必然会使任务调度程序陷入困境,因为它必须跟踪它们(自上次打勾以来已经可以运行,等等)。