通过并行 提高批量审核PDF性能

时间:2022-01-28 21:22:36

  上一篇文章提到了 通过 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.大多数静态方法都是可同时从多个线程中调用。  但是,即使在这些情况下,所涉及到的同步也可能导致速度大幅减慢。

  

工作点滴,持续提升