问题描述:

在最近的一个项目中,有一段代码逻辑是对一个数组中的所有元素调用相同的处理函数,并返回结果,且这个处理函数耗时比较久,典型的可以并行化处理的例子。

解决思路:

上述问题,可以简化成如何并行执行map函数或者并行执行foreach函数,幸运的是,在.Net 4.0中,这两个函数都有并行版本,分别是PLINQ和Parallel.ForEach。

解决细节:

  1. 并行化的map

    在C#中,是没有函数式编程中的mapreducefilter等函数,但这几个函数都可以通过LINQ来做到,对应关系是 map <-> Selectreduce <-> Aggregatefilter <-> Where。并且,LINQ支持链式操作,再加上C#也支持匿名函数,写起来非常清爽干净。比如,需要计算班里所有男生成绩的总和,只需要

    1
    var scoreSum = students.Select(stu => stu.score).Where(stu => stu.gender == "male").Aggregate((sum, next) => sum + next);

    更酷的是,在链式调用中,加上.AsParallel(),整个操作过程就变成并行版本了,也就是前面说到的PLINQ。

  2. 并行化的ForEach

    Parallel.ForEachForEach的并行版,调用规则也非常简单。例如,打印班里所有男生的成绩,使用Parallel.ForEach实现如下:

    1
    2
    3
    4
    5
    6
    7
    Parallel.ForEach(students, stu =>
    {
    if (stu.gender == "male")
    {
    Console.WriteLine(stu.score);
    }
    });
  3. 比较

    关于这两种并行解决方案到底需用哪个,这里有篇文章详细对比了这两种解决方案的适用场景。我自己使用PLINQ来实现,仅用一行代码就解决了我的问题,形式如下:

    1
    var result = list.AsParallel().AsOrdered().Select(x => DoSomething(x)).ToList();

    其中特别要注意的是,加上.AsOrdered()保证返回结果中result与输入list中的值是一一对应的。