上一篇文章提到了 通过 iTextSharp 实现PDF 审核盖章 ,如果当需要一次审核大批量的PDF我们如何来提高程序的性能呢?
下面我们通过并行计算来提升性能。
首先是一个审核PDF的方法
public class PDFManage { public string PDFApprove(string path) { //内部实现参见上一篇文字
//返回是新的PDF路径 } }
然后是普通的实现,我们拿到一个所有需要审核的PDF Path 集合 IList<string> pathLists
PDFManage pdfManage = new PDFManage(); foreach (var item in pathLists) { pdfManage.PDFApprove(item); }
下面是并行的实现
Parallel.ForEach(pathLists, //可枚举的数据源 (itemPath, loopState) => //Action<TSource, ParallelLoopState> 的lambda表达式 形式 :将为每个迭代调用一次的委托 { PDFManage pdfManage = new PDFManage(); pdfManage.PDFApprove(itemPath); });
为什么要把 PDFManage 实例放在每次迭代里面呢? 为了避免写入共享内存位子,每当多个线程同时访问时,都很有可能出现争用条件。 即使您可以使用锁来同步访问,同步开销也可能会对性能造成损害
当我需要有拿到每一个审核后的新的PDF路径一个如何做呢?
普通的实现我在这里就不写了,使用线程局部变量 Parallel.ForEach 循环来实现
IList<string> ApproveLists = new List<string>();
Parallel.ForEach(pathLists, //source 可枚举的数据源 () => { return new List<string>(); }, //用于返回每个任务的本地数据的初始状态的函数委托 (itemPath, loop, ApprovePaths) => //将为每个迭代调用一次的委托 { PDFManage pdfManage = new PDFManage(); ApprovePaths.Add(pdfManage.PDFApprove(itemPath)); return ApprovePaths; }, (finalResult) => //用于对每个任务的本地状态执行一个最终操作的委托(每个线程结束时最总会到这里来) { foreach (var item in ApproveLists) { finalResult.Add(item); } //以原子操作的形式,将指定ApproveLists变量设置为指定finalResult值 Interlocked.Exchange(ref ApproveLists, finalResult); });
使用并行的时候还需要注意: 不要假定并行始终速度更快,并行循环可能比顺序循环的运行速度慢。具有很少迭代和快速用户委托的并行循环未必会快很多。
计算机上的处理器数限制了并行化的优点。 在仅仅一个处理器上运行多个主要进行计算的线程时,速度并不会得到提升。
所以我们要加一些设定来优化一下
// 获取当前服务器处理器数量 int procCount = System.Environment.ProcessorCount; // 获取当前集合源的数量 int ListCount = pathLists.Count();
// 通过判断服务器的处理数量, 已经集合源的数量来决定是否需要进行并行计算。
总结一下使用并行:
1.并行不一定更快,源的数量、委托的操作有关。
2.并行时每个迭代调用内部不要有共享内存位子(简单说就是单线程和多线程争用条件的问题)
3.大多数静态方法都是可同时从多个线程中调用。 但是,即使在这些情况下,所涉及到的同步也可能导致速度大幅减慢。
工作点滴,持续提升