你会在这里遇到几个问题。调度程序的饥饿回避机制会将您的任务视为等待进程时被阻止。它会发现很难区分死锁线程和简单等待进程完成的线程。因此,如果您的任务运行或很长时间(见下文),它可能会安排新的任务。登山启发式应考虑系统的整体负载,无论是从应用程序还是其他应用程序。它只是试图最大限度地完成工作,所以它会增加更多的工作,直到系统的整体吞吐量停止增加,然后退出。我不认为这将影响您的应用程序,但避免问题的可能会。
你可以找到更多的细节,如何这在Parallel Programming with Microsoft®.NET,科林坎贝尔,拉尔夫·约翰逊,米勒阿德,斯蒂芬Toub(是online早期草案)所有的作品。
“的.NET线程池自动根据管理工作者在游泳池 线程数它添加和删除线程内置 启发式的.NET线程池具有注入 线程两种主要机制:A饥饿避免机制,增加了工人 线程,如果它看到在排队的物品,并试图在使用作为 几个线程尽可能地最大化吞吐量爬山 启发而毫无进展。
饥饿避税的目的是为了防止死锁这种类型的死锁可能发生在工作人员th读取等待同步 事件,该事件只能由线程池的全局或本地队列中仍处于待执行 的工作项目满足。如果有一个固定的 工作者线程数,并且所有这些线程同样被阻止,则系统将无法取得进一步的进展。 添加新的工作线程可解决问题。
爬山启发式算法的一个目标是在线程被I/O或其他等待条件阻塞的等待条件 阻止处理器时提高内核的利用率 。默认情况下,托管线程池每个核心有一个 工作线程。如果其中一个工作线程变为 被阻止,则核心可能未充分利用,这取决于计算机整体工作负载上的 。线程注入逻辑 不区分被阻塞的线程和正在执行冗长的处理器密集型操作的线程 。因此,每当线程池的全局或本地队列包含待定的 工作项时,需要花费很长时间运行的活动工作项(大于 半秒)才会触发新线程池工作线程的创建 线程。
.NET线程池有机会每工作项完成 时间或以500毫秒间隔注入线程,无论哪个 更短。线程池使用此机会尝试添加线程 (或将它们带走),并根据线程数的以前更改的反馈进行指导。如果添加线程似乎有助于吞吐量,则线程池会增加更多;否则,它会减少工作线程的数量。这种技术被称为爬山启发式。 因此,保持单个任务简短的一个原因是为了避免 “饥饿检测”,但是另一个保持简短的原因是 给线程池更多的机会来提高吞吐量,调整线程数量为 。单个任务的持续时间越短,线程池可以测量吞吐量的频率越高,并且相应地调整线程计数。
为了使这个具体,请考虑一个极端的例子。假设你有一个复杂的财务模拟和 操作,其中每个操作需要花费10分钟的时间才能完成,需要500个处理器密集的 操作。如果您在全局队列中为这些操作的每个 创建顶级任务,则会发现大约五分钟后,线程池将增长到500个工作线程。原因在于 线程池将所有任务视为被阻止,并开始以每秒大约两个线程的速率添加新的线程。
500工作线程有什么问题?原则上,没有任何内容,如果你有500个内核供他们使用,并且海量内存系统为 。事实上,这是并行计算的长期愿景。 但是,如果您的计算机上没有多个内核,则在多个线程正在竞争时间片的情况下,您的系统为 。这种情况被称为处理器超额订购。允许许多处理器密集型线程在单个内核上竞争时间会增加上下文切换开销,这会严重降低整个系统的吞吐量。即使你没有耗尽内存,在这个 的情况下的性能可能会比连续计算更糟糕得多。 (每个上下文切换需要6,000到8,000个处理器周期。) 上下文切换的开销不是唯一的开销来源。 .NET中的托管线程占用大约1兆字节的堆栈空间,无论该空间是否用于当前正在执行的功能。 需要大约200,000个CPU周期来创建一个新线程,并且大约需要100,000个周期来退出一个线程。这些是昂贵的操作。
只要你的任务不需要每个分钟,线程池的登山算法就会最终意识到它有太多线程 并自行减少。但是,如果你的任务 占用一个工作线程几秒或几分钟或几小时,那么 将抛出线程池的启发式,此时 应考虑替代方案。
第一个选项是将您的应用程序分解为更短的 任务,这些任务的完成速度足以使线程池成功执行 控制线程数以获得最佳吞吐量。 第二种可能性是实现您自己的任务调度程序 不执行线程注入的对象。如果您的任务持续时间很长,则不需要高度优化的任务调度程序,因为与任务的执行时间 相比,调度的成本可以忽略不计。 MSDN®开发人员计划有一个 简单任务调度程序实现的示例,该实现限制并发的最大度数 。有关更多信息,请参阅本章末尾的“进一步阅读”部分, 。
作为最后的手段,您可以使用SetMaxThreads方法 配置ThreadPool类有上限的工作线程的数量 ,通常等于(这是 Environment.ProcessorCount属性)核心数量。此上限适用于 的整个过程,包括所有的应用程序域“
当然,我应该做的是将一个TaskCompletionSource连接到Process.Exited事件,然后有一个返回Task的TranscodeAsync方法。它会是非阻塞的。然后,我可以对任务进行更细致的控制,同时仍然呆在第三方物流的粮食中。 – 2012-02-03 14:19:17