上一篇《C#中多线程的那点事-Parallel》,我们讲述了一种全新的数据并行处理方法Parallel,Parallel也是 TPL(Task Parallel Library)提供的一个类。其提供的Invoke可以非常方便的开启并行任务;For,Foreach可以非常方便的对大量数据进行并行处理。
小明同学,昨天回家练习了Parallel的使用,不禁感叹,C#真的是处处为开发者着想。当你觉得代码啰嗦复杂的时候,也许应该去查一查有没有用C#更简单的实现方法!
我告诉小明,听了今天的课程,以前那些简单方法,都是小儿科!
小明听了我这话之后,两眼放光,仿佛在说,那你还不开始。。。
基础补充
在正式开始之前,我们先简单说一下C#中的扩展方法。这是一种可以给类和接口,动态添加扩展函数的机制。利用扩展方法,可以在不改变已有代码的前提下,扩展类和接口的功能。
可能很多同学觉得不可思议,怎么可能呢!刚开始接触的时候,外老师也是这么觉得的。不过很多事情,我们觉得不好实现,但是搞编译器的专家们,却觉得不是问题。
如果已经了解了C#的扩展函数的同学,可以直接跳到下一节。
先看个简单的实例:
// 集合扩展类
public static class ListExt
{
// 计算集合平均值
public static int Avg(this List<int> list)
{
int sum = 0;
foreach (var l in list)
{
sum += l;
}
return sum / list.Count;
}
}
注意,以上的ListExt类,有3个特点:
- ListExt是一个staitc类
- 函数Avg是一个static函数
- Avg的参数list前面,添加了this关键字
没错,以上3点,就是C#中的扩展函数的基本语法。这样我们在不修改List这个类原有代码的情况下,给List<int>添加了一个Avg函数,来计算集合中全部数据的平均值。
我们这里为了简单起见,先不考虑模板和计算溢出。
另外,我们看到扩展函数,实质上还是一个静态的函数,在扩展函数中,也只能访问原来的类的公有成员。
那这个Avg函数如何使用呢:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello LINQ World!");
var list = new List<int>();
for (int i = 0; i < 100; i++)
{
list.Add(i);
}
var avg = list.Avg();
Console.WriteLine(#34;Average: {avg}");
}
}
可以看到,和使用List<int>的其他函数一模一样。
其实扩展函数,就是利用静态函数,包装的一种语法糖,方便我们更直观的使用。这种操作看似平淡无奇,但是在微软的加持下,就发挥出了无穷的威力。请接着往下看。
LINQ
集合,包括List,Dictionary,HashSet等,是我们日常编程中使用最广泛的数据结构。而这些类本身,虽然是泛型的,支持各种数据的集合。但其本身提供的功能函数有限。
就比如前文的求平均值的功能,我们每次都要编写一次循环,或者需要封装辅助类。代码冗余,使用不直观。
微软在编程语言方面,确实是处处为程序员着想,他们早就注意到了这个问题。也给出了他们的解决方案,就是LINQ。
LINQ是一组基于C#扩展函数实现的通用集合处理函数合集。其中包括了大多数对于集合的常规操作:
- 元素访问函数,比如First,Last,ElementAt,Single,FirstOrDefault
- 统计相关函数,比如Average,Min,Max,Sum,Aggregate(聚合,很好用)
- 逻辑判断函数,比如All,Any,Contains,SequenceEqual(判断集合相等)
- 集合操作函数,比如Select,Where,Append,OrderBy,Concat,Distinct,Except,Intersect,Join,Reverse,Union
- 分组变换函数,比如ToDictionary,ToLookup,ToHashSet,GroupBy
- 骚操作函数,比如Cast,OfType,Empty,Prepend,Range,Repeat,ThenBy,Zip
以上是常用的集合相关函数,还有一些不常用的函数,就不一一列举了。总之有一点,有了这些函数,我们操作集合会方便太多,效率大增。绝对是干货。LINQ就是传说中的那种用过之后就回不去的API合集。
这些特性,在其他编程语言看来一定是这样的:
我看了一眼小明,跃跃欲试的样子,像极了当年的我!
PLINQ
今天的内容有点多,我让大家休息了一会,消化消化。没过一会,小明来找我说道:
老师,LINQ中的操作是并行的吗?
不得不说,小明学习多线程是有点着魔了,不过我非常喜欢他这个状态。
LINQ的操作,是串行的。不过微软当然也想到了小明的这个需求,于是他们又搞了一个PLINQ。
PLINQ就是并行的LINQ的意思。其API基本和LINQ相同,微软对PLINQ的介绍,也说PLINQ是LINQ的并行替代实现。
来看一个简单的PLINQ的实例程序:
static void Main(string[] args)
{
Console.WriteLine("Hello PLINQ World!");
var list = new List<int>();
for (int i = 0; i < 10; i++)
{
list.Add(i);
}
var plist = list.AsParallel();
var avg = plist.Average();
Console.WriteLine(#34;PLINQ Average: {avg}");
plist.ForAll(l =>
{
Console.WriteLine(#34;value: {l}, " +
#34;tid: {Thread.CurrentThread.ManagedThreadId}");
});
}
程序中通过PLINQ的AsParallel函数,将普通的集合,转换为一个并行的集合。程序输出如下:
从结果中可以看出,PLINQ的并行遍历集合,和ThreadPool及Parallel运行的机制相同。但是PLINQ的功能要强大得多。
看到这里,相信你一定已经被C#的魅力所吸引。这也是C#在编程语言中,可以一直排在前5的原因之一。
布置作业
练习PLINQ的使用。
系列文章
下面是给同学们准备的干货: