用 C# 进行多线程编程有很多方式,比如使用 Thread 对象开启一个新线程,但这已经是一种落后的写法了,
现在推荐的写法是使用 Parallel 类,它可以让我们像写传统代码一样编写多线程的程序,Parallel 类有三个常用
的方法如下:
Parallel.For():开启多线程循环执行一段代码
Parallel.ForEach():开启多线程遍历处理一个对象集合
Parallel.Invoke():开启多线程执行一个方法
下面我们来看看如何使用它们。
新建一个.net core的 Console 项目,在项目根目录下新增类 ConcurrencyDemo.cs 用来演示,如下:
在 ConcurrencyDemo.cs 中新增方法 ParallelForPrint(),其作用是调用 Parallel.For() 循环输出1~9,如下:
public void ParallelForPrint() { Parallel.For(1, 10, i => { Console.WriteLine(string.Format(" i = {0}", i)); }); }
此方法有3个参数,第1个是循环的起始值,第2个是结束值,第3个是要执行的动作,这里用 Lambda 表达式输出 i.
在 Program.cs 的 Main() 方法中调用,代码如下:
static void Main(string[] args) { Console.WriteLine("Hello World!"); ParallelDemo pdemo = new ParallelDemo(); pdemo.ParallelForPrint(); Console.ReadLine(); }
编译后运行程序,得到如下结果:
可以看到,并没有按顺序输出1~9,这从侧面说明是使用了多线程做循环输出。
下面我们将方法稍作修改,并与 for 循环做一下对比,看使用 Parallel.For() 做多线程编程比用传统的 for 能快多少。
public void ParallelForCompareFor() { //计算 Parallel.For() 的时间 int total1 = 0; Stopwatch watch1 = new Stopwatch(); watch1.Start(); //开始计时 Parallel.For(1, 20000, i => { total1 += i; }); watch1.Stop(); //结束计时 Console.WriteLine(string.Format(" Parallel.For 循环花了 {0} 毫秒。",watch1.ElapsedMilliseconds));
//计算 for() 的时间 int total2 = 0; Stopwatch watch2 = new Stopwatch(); watch2.Start(); //开始计时 for(int j=1;j<20000;j++) { total2 += j; } watch2.Stop(); //结束计时 Console.WriteLine(string.Format(" for 循环花了 {0} 毫秒。", watch2.ElapsedMilliseconds)); }
在 Program.cs 的 Main() 方法中调用,代码如下:
static void Main(string[] args) { Console.WriteLine("Hello World!"); ParallelDemo pdemo = new ParallelDemo(); pdemo.ParallelForCompareFor(); Console.ReadLine(); }
编译后运行程序,得到如下结果:
和我们期望的不一样,使用 Parallel.For() 花费了更多的时间。
如果我们将循环的次数由 20000 改为 20 时候,结果如下:
使用 Parallel.For() 花费的时间依然比 for 多。
这说明 Parallel.For() 的使用是有条件的,如果循环内的动作运行的时间很短,它反而更慢,这是因为使用
多线程的时候,线程的创建、撤销等是有时间开销的,这一点在本系列中的第2篇中有过说明。
下面我们再对方法做一下修改,在循环体内让当前线程休眠 10 毫秒,代码如下,看看会发生什么:
public void ParallelForCompareFor() { //计算 Parallel.For() 的时间 int total1 = 0; Stopwatch watch1 = new Stopwatch(); watch1.Start(); //开始计时 Parallel.For(1, 20000, i => { total1 += i; Thread.Sleep(10); }); watch1.Stop(); //结束计时 Console.WriteLine(string.Format(" Parallel.For 循环花了 {0} 毫秒。",watch1.ElapsedMilliseconds)); //计算 for() 的时间 int total2 = 0; Stopwatch watch2 = new Stopwatch(); watch2.Start(); //开始计时 for(int j=1;j< 20000; j++) { total2 += j; Thread.Sleep(10); } watch2.Stop(); //结束计时 Console.WriteLine(string.Format(" for 循环花了 {0} 毫秒。", watch2.ElapsedMilliseconds)); }
编译后运行结果如下:
可以看到使用 Parallel.For() 的优势便体现出来了。我们将循环次数由 20000 改成 200,结果如下:
使用 Parallel.For() 会快4倍左右。如果再进一步调整休眠时间为 1 毫秒,运行结果如下:
从上面的演示可以看到,Parallel.For() 开启多线程比 for 快的前提是循环体中的代码执行要有一定的
时间开销,否则是达不到更快的效果的。