我需要编写一个GUI应用程序来处理外部命令行工具上的一些文件。我需要通过文件将它们并行化,并在CPU的线程上进行限制,以最大程度地提高cpu的使用率和吞吐量。我做了一些工作和一些研究:
当我第一次在StackOverflow上问这个问题时,有人建议我使用 Parallel.Foreach 。它确实起作用;但是它只是阻塞了一些线程,浪费了CPU等待外部进程。而且,如果外部进程运行很长时间,则会减少并行处理的线程!所以最后我放弃了使用此方法,并试图找到其他解决方案。
我只是使用
SemaphoreSlim sem = new SemaphoreSlim(Environment.ProcessorCount);
限制外部进程的数量 并使用
await task.whenall(tasks);
等待所有过程而不会阻塞我的GUI程序。
现在我正在使用它。效果很好。
但是只有一个问题:在MSDN中提到,当等待时间预计非常短时,信号量管理器是为单个进程设计的。 但是在我的外部过程中,它通常运行很长时间(处理时间取决于输入文件的类型和大小)。因此,在我的情况下, Spinwait 浪费了CPU资源。因此,我真的很想知道是否有一些解决方案可以避免这种轮转等待,但是直到现在我仍然找不到。有人可能会说使用传统的信号量。我试过了。但是信号量无法等待,因此它阻塞了我的GUI,如果我使用
await Task.run()
使用它,那么它的表现并不比 semaphoreslim 好。
我发现的另一个解决方案是使用TPL数据流库。它确实比 semaphoreslim 稍好。但是我的某些特定用例无法在TPL Dataflow中实现。
例如,我有一堆档案。我需要解压缩它们并处理每个存档中的文件,然后重新压缩。在TPL Dataflow中,我想拆分为“解压缩块”(palarism:1),“文件处理块”(palarism:12)和“ compress block”(palarism:1)。但是我不知道如何等待TPL Dataflow的all任务中的某些任务。如果我的理解没错,TPL Dataflow可以等到块完成为止。在我的情况下,如果处理了存档1的文件,则compress块无法知道它。它需要等待,直到所有文件都已处理。
但是在信号量中,我可以使用
await Task.whenall(someoftasks);
在每个foreach归档文件迭代中等待它们。这样我可以获得更高的吞吐量,所以我最终放弃了使用TPL数据流。
因此,在我研究之后,我仍在使用semaphoreslim。它工作得很好,但是我为浪费CPU资源而感到困惑。因此,我想知道是否有更好的方法来限制c#中的外部过程。