c# 并行运算
1. Parallel.INVOKE() 看实例:
private static Stopwatch watch = new Stopwatch(); private static void Run1() { Thread.Sleep(2000); Console.WriteLine("Task 1 takes 2 sec"); } private static void Run2() { Thread.Sleep(3000); Console.WriteLine("Task 2 takes 3 sec"); } static void Main(string[] args) { watch.Start(); Parallel.Invoke(Run1,Run2); watch.Stop(); Console.WriteLine("Parallel run "+watch.ElapsedMilliseconds +" ms"); watch.Restart(); Run1(); Run2(); watch.Stop(); Console.WriteLine("Normal run "+watch.ElapsedMilliseconds+" ms"); Console.ReadLine();
看结果:
2.Parallel.For
看实例:
watch.Start(); for (int i = 0; i < 10000; i++) { for (int j = 0; j < 5000; j++) { int sum = 0; sum++; } } watch.Stop(); Console.WriteLine("Normal run " + watch.ElapsedMilliseconds + "ms"); watch.Restart(); watch.Start(); Parallel.For(0, 1000, item => { for (int j = 0; j < 5000; j++) { int sum = 0; sum += item; } }); watch.Stop(); Console.WriteLine("ParalleFor run " + watch.ElapsedMilliseconds + "ms"); Console.ReadLine();
看结果:
是不是Parallel.For在任何时候都比for要快呢?答案当然是“不是”,要不然微软还留着for干嘛?
看实例:
var obj = new object(); long num = 0; ConcurrentBag<long> bag = new ConcurrentBag<long>(); watch.Start(); for (int i = 0; i < 10000; i++) { for (int j = 0; j < 5000; j++) { // int sum = 0; // sum++; num++; } } watch.Stop(); Console.WriteLine("Normal run "+watch.ElapsedMilliseconds+ "Ms"); watch.Restart(); Parallel.For(0, 1000, item => { for (int j = 0; j < 5000; j++) { //int sum = 0; //sum += item; lock (obj) { num++; //全局变量,就要考虑到线程安全了 //这主要是由于并行同时访问全局变量,会出现资源争夺,大多数时间消耗在了资源等待上面。 } } }); watch.Stop(); Console.WriteLine("ParalleFor run " + watch.ElapsedMilliseconds + "ms"); Console.ReadLine();
结果:(结果不是稳定的,你懂得~)
再看代码:
Parallel.For(0, 100, i => { Console.Write( i +" "); }); Console.ReadLine();
再看结果:
傻孩子,这样你懂了吧~
3.Parallel.Foreach
//Environment.ProcessorCount能够获取到当前的硬件线程数,所以这里也就开了2个区。 Console.WriteLine(Environment.ProcessorCount); Console.ReadLine(); //继续我的并发编程; //可以将数据进行分区,每一个小区内实现串行计算;分区采用Create实现; for (int j = 1; j < 4; j++) { Console.WriteLine(" 第{0}次比较", j); ConcurrentBag<int> bag = new ConcurrentBag<int>(); watch.Start(); for (int i = 0; i < 300000; i++) { bag.Add(i); } Console.WriteLine("串行计算:集合有{0},总共耗时:{1}",bag.Count,watch.ElapsedMilliseconds); GC.Collect(); bag = new ConcurrentBag<int>(); watch.Restart(); Parallel.ForEach(Partitioner.Create(0, 300000), i => { for (int m = i.Item1; m < i.Item2; m++) { bag.Add(m); } }); Console.WriteLine("并行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds); GC.Collect(); watch.Stop(); } Console.ReadLine();
结果:
4.parallel 中途退出循环
Break: 当然这个是通知并行计算尽快的退出循环,比如并行计算正在迭代100,那么break后程序还会迭代所有小于100的。
Stop:这个就不一样了,比如正在迭代100突然遇到stop,那它啥也不管了,直接退出。
ConcurrentBag<long> bag = new ConcurrentBag<long>(); watch.Start(); Parallel.For(0,1000, (i, state) => { if (bag.Count == 300) { state.Break(); //当数量达到300个时,会立刻停止;可以看到结果"Bag count is 300",如果用break,可能结果是300多个或者300个,大家可以测试一下。 return; } bag.Add(i); }); watch.Stop(); Console.WriteLine("Bag count is "+bag.Count+" times is "+watch.ElapsedMilliseconds);
异常处理
由于Task的Start方法是异步启动的,所以我们需要额外的技术来完成异常处理
try { var parallelExceptions = new ConcurrentQueue<Exception>(); Parallel.For(0, 1, (i) => { try { throw new InvalidOperationException("并行任务中出现的异常"); } catch (Exception e) { parallelExceptions.Enqueue(e); } if (parallelExceptions.Count > 0) throw new AggregateException(parallelExceptions); }); } catch (AggregateException err) { foreach (Exception item in err.InnerExceptions) { Console.WriteLine("异常类型:{0}{1}来自: {2}{3}异常内容:{4}", item.InnerException.GetType(), Environment.NewLine, item.InnerException.Source, Environment.NewLine, item.InnerException.Message); } } Console.WriteLine("主线程马上结束"); Console.ReadKey();
static void Main(string[] args) { try { Parallel.Invoke(Run1, Run2); //这个捕获 //在不同的模式下,会有不同结果地呀; //debug 模式下,会停止的 //realse 模式下就可以获取异常; } catch (AggregateException ex) { foreach (var single in ex.InnerExceptions) { Console.WriteLine(single.Message); } } Console.Read(); } static void Run1() { Thread.Sleep(3000); throw new Exception("我是任务1抛出的异常"); } static void Run2() { Thread.Sleep(5000); throw new Exception("我是任务2抛出的异常"); }
实例二
try { go1(); //这样的异常只能捕获其中一个地呀; go2(); } catch (Exception ex) { Console.WriteLine(ex.Message); } watch.Stop(); Console.WriteLine("Normal run :" + watch.ElapsedMilliseconds); //尼玛的,这样的异常,居然捕获不到的地呀
默认的情况下,底层机制会尽可能多的使用硬件线程,然而我们使用手动指定的好处是我们可以在2,4,8个硬件线程的情况下来进行测量加速比。
class Program { static void Main(string[] args) { var bag = new ConcurrentBag<int>(); ParallelOptions options = new ParallelOptions(); //指定使用的硬件线程数为1 options.MaxDegreeOfParallelism = 1; Parallel.For(0, 300000, options, i => { bag.Add(i); }); Console.WriteLine("并行计算:集合有:{0}", bag.Count); } }