5天玩转C#并行和多线程编程 —— 第一天 认识Parallel
目录
5天玩转C#并行和多线程编程 —— 第一天 认识Parallel
5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结
5天所有的Demo:Demo
随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能。
在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Tasks。这里面有很多关于并行开发的东西,今天第一篇就介绍下最基础,最简单的——认识和使用Parallel类。
一、 Parallel类(提供对并行循环和区域的支持)的使用
在Parallel类下有三个常用的方法Invoke,For,ForEach
1. Parallel.Invoke:尽可能并行执行提供的每个操作(Executes each of the provided actions, possibly in parallel)
微软官方对该方法的作用表达很明确了,就是尽可能的同时执行你提供的方法
下面来看一个例子:新建一个控制台程序
- static void Main(string[] args)
- {
- #region Demo1
- Stopwatch stopwatch = new Stopwatch();
- Console.WriteLine("Normal:");
- stopwatch.Start();
- RunOne();
- RunTwo();
- stopwatch.Stop();
- Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
- Console.WriteLine("----------------------------");
- Console.WriteLine("Parallel:");
- stopwatch.Restart();
- Parallel.Invoke(RunOne, RunTwo);
- stopwatch.Stop();
- Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
- #endregion
- Console.ReadKey();
- }
- static void RunOne()
- {
- Thread.Sleep(2000);
- Console.WriteLine("The RunOne cost 2 seconds.");
- }
- static void RunTwo()
- {
- Thread.Sleep(3000);
- Console.WriteLine("The RunTwo cost 3 seconds.");
- }
结果如下:
- Normal:
- The RunOne cost 2 seconds.
- The RunTwo cost 3 seconds.
- Normal cost 5001 milliseconds
- ----------------------------
- Parallel:
- The RunOne cost 2 seconds.
- The RunTwo cost 3 seconds.
- Parallel cost 3010 milliseconds
应该能够猜到,正常调用的话应该是5秒多,而Parallel.Invoke方法调用用了只有3秒,也就是耗时最长的那个方法,可以看出方法是并行执行的,执行效率提高了很多。
2. Parallel.For:执行 for(在 Visual Basic 中为 For)循环,其中可能会并行运行迭代(Executes a for (For in Visual Basic) loop in which iterations may run in parallel.)
这个方法和For循环功能相似,来写个例子看一下吧,还是控制台程序
- Stopwatch stopwatch=new Stopwatch();
- Console.WriteLine("Normal:");
- stopwatch.Start();
- for (int i = 0; i < 10000; i++)
- {
- for (int j = 0; j < 60000; j++)
- {
- int sum = 0;
- sum += i;
- }
- }
- stopwatch.Stop();
- Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
- Console.WriteLine("----------------------------");
- Console.WriteLine("Parallel:");
- stopwatch.Restart();
- Parallel.For(0, 10000, i =>
- {
- for (int j = 0; j < 60000; j++)
- {
- int sum = 0;
- sum += i;
- }
- });
- stopwatch.Stop();
- Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
- Normal:
- Normal cost 1682 milliseconds
- ----------------------------
- Parallel:
- Parallel cost 575 milliseconds
可以看到,Parallel.For所用的时间比单纯的for快了1秒多,可见提升的性能是非常可观的。那么,是不是Parallel.For在任何时候都比for要快呢?答案当然是“不是”,要不然微软还留着for干嘛?
修改一下代码:
- object o=new object();
- long sum = 0;
- Stopwatch stopwatch = new Stopwatch();
- Console.WriteLine("Normal:");
- stopwatch.Start();
- for (int i = 0; i < 10000; i++)
- {
- for (int j = 0; j < 60000; j++)
- {
- //int sum = 0;
- //sum += i;
- sum++;
- }
- }
- stopwatch.Stop();
- Console.WriteLine("Normal cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
- Console.WriteLine("----------------------------");
- Console.WriteLine("Parallel:");
- stopwatch.Restart();
- Parallel.For(0, 10000, i =>
- {
- for (int j = 0; j < 60000; j++)
- {
- //int sum = 0;
- //sum += i;
- lock (o)
- {
- sum++;
- }
- }
- });
- stopwatch.Stop();
- Console.WriteLine("Parallel cost " + stopwatch.ElapsedMilliseconds + " milliseconds");
- Normal:
- Normal cost 2549 milliseconds
- ----------------------------
- Parallel:
- Parallel cost 21563 milliseconds
一直说并行,那么从哪里可以看出来Parallel.For是并行执行的呢?下面来写个测试代码:
- Parallel.For(0, 100, i =>
- {
- Console.WriteLine(i);
- });
- for (int i = 0; i < 100; i++)
- {
- Console.WriteLine(i);
- }
从0输出到99,运行后会发现输出的顺序不对,用for顺序肯定是对的,并行同时执行,所以会出现输出顺序不同的情况。
3. Parallel.ForEach:执行 foreach(在 Visual Basic 中为 For Each)操作,其中在 IEnumerable 上可能会并行运行迭代(Executes a foreach (For Each in Visual Basic) operation on an IEnumerable in which iterations may run in parallel.)
这个方法跟Foreach方法很相似,看看使用方法
- List<string> myList = new List<string>();
- Parallel.ForEach(myList, p =>
- {
- DoSomething(p);
- });
二、 Parallel类中途退出循环和异常处理
1. 当我们使用到Parallel类,必然是处理一些比较耗时的操作,当然也很耗CPU和内存,如果我们中途想停止,怎么办呢?
在串行代码中我们break一下就搞定了,但是并行就不是这么简单了,不过没关系,在并行循环的委托参数中提供了一个ParallelLoopState类的实例,该实例提供了Break和Stop方法来帮我们实现。
Break:告知 Parallel 循环应在系统方便的时候尽早停止执行当前迭代之外的迭代。
Stop:告知 Parallel 循环应在系统方便的时候尽早停止执行。
下面来写一段代码使用一下:
- ConcurrentBag<int> bag = new ConcurrentBag<int>();
- Stopwatch stopWatch = new Stopwatch();
- stopWatch.Start();
- Parallel.For(0, 1000, (i, state) =>
- {
- if (bag.Count == 300)
- {
- state.Break();
- //state.Stop();
- return;
- }
- bag.Add(i);
- });
- stopWatch.Stop();
- Console.WriteLine("Bag count is " + bag.Count + ", " + stopWatch.ElapsedMilliseconds);
2. 异常处理
首先任务是并行计算的,处理过程中可能会产生n多的异常,那么如何来获取到这些异常呢?普通的Exception并不能获取到异常,然而为并行诞生的AggregateExcepation就可以获取到一组异常。
- try
- {
- Parallel.Invoke(RunOne, RunTwo);
- }
- catch (AggregateException aex)
- {
- foreach (var ex in aex.InnerExceptions)
- {
- Console.WriteLine(ex.Message