Parallel 的使用 parallel.Invoke
var stopWatch = new StopWatch(); //创建一个Stopwatch实例
stopWatch.Start(); //开始计时
stopWatch.Stop(); //停止计时
stopWatch.Reset(); //重置StopWatch
stopWatch.Restart(); //重新启动被停止的StopWatch
stopWatch.ElapsedMilliseconds //获取stopWatch从开始到现在的时间差,单位是毫秒
class Program
{
static void Main(string[] args)
{
var watch = Stopwatch.StartNew();
watch.Start();
Run1();
Run2();
Console.WriteLine("我是串行开发,总共耗时:{0}\n", watch.ElapsedMilliseconds);
watch.Restart();
Parallel.Invoke(Run1, Run2);
watch.Stop();
Console.WriteLine("我是并行开发,总共耗时:{0}", watch.ElapsedMilliseconds);
Console.Read();
}
private static void Run1()
{
Console.WriteLine("我是任务一,我跑了1s");
Thread.Sleep(1000);
}
private static void Run2()
{
Console.WriteLine("我是任务二,我跑了2s");
Thread.Sleep(2000);
}
在这个例子中可以获取二点信息:
第一:一个任务是可以分解成多个任务,采用分而治之的思想。
第二:尽可能的避免子任务之间的依赖性,因为子任务是并行执行,所以就没有谁一定在前,谁一定在后的规定了。
Parallel.for
class Program
{
static void Main(string[] args)
{
for(int j=1;j<4;j++)
{
Console.WriteLine("\n第{0}次比较", j);
ConcurrentBag<int> bag = new ConcurrentBag<int>();
var watch = Stopwatch.StartNew();
watch.Start();
for(int i=0;i<20000000;i++)
{
bag.Add(i);
}
Console.WriteLine("串行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);
GC.Collect();
bag = new ConcurrentBag<int>();
watch = Stopwatch.StartNew();
watch.Start();
//Parallel.for的步行是1
Parallel.For(0, 20000000, i =>
{
bag.Add(i);
});
Console.WriteLine("并行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);
GC.Collect();
}
}
}
Parallel.forEach
static void Main(string[] args)
{
for(int j=1;j<4;j++)
{
Console.WriteLine("\n第{0}次比较", j);
ConcurrentBag<int> bag = new ConcurrentBag<int>();
var watch = Stopwatch.StartNew();
watch.Start();
for(int i=0;i<20000000;i++)
{
bag.Add(i);
}
Console.WriteLine("串行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);
GC.Collect();
bag = new ConcurrentBag<int>();
watch = Stopwatch.StartNew();
watch.Start();
Parallel.ForEach(Partitioner.Create(0, 20000000), i =>
{
for (int m = i.Item1; m < i.Item2; m++)
{
bag.Add(m);
}
});
Console.WriteLine("并行计算:集合有:{0},总共耗时:{1}", bag.Count, watch.ElapsedMilliseconds);
GC.Collect();
}
}
注意
<1> 如何中途退出并行循环?
在串行代码中我们break一下就搞定了,但是并行就不是这么简单了,不过没关系,在并行循环的委托参数中提供了一个
ParallelLoopState,该实例提供了Break和Stop方法来帮我们实现。
Break: 当然这个是通知并行计算尽快的退出循环,比如并行计算正在迭代100,那么break后程序还会迭代所有小于100的。
Stop:这个就不一样了,比如正在迭代100突然遇到stop,那它啥也不管了,直接退出。
static void Main(string[] args)
{
for(int j=1;j<4;j++)
{
var watch = Stopwatch.StartNew();
watch.Start();
ConcurrentBag<int> bag = new ConcurrentBag<int>();
Parallel.For(0, 20000000, (i, state) =>
{
if(bag.Count==1000)
{
state.Break();
return;
}
bag.Add(i);
});
Console.WriteLine("当前集合有{0}个元素。", bag.Count);
}
}
<2> 并行计算中抛出异常怎么处理?
首先任务是并行计算的,处理过程中可能会产生n多的异常,那么如何来获取到这些异常呢?普通的Exception并不能获取到异常,然而为并行诞生的AggregateExcepation就可以获取到一组异常
class Program
{
static void Main(string[] args)
{
try
{
Parallel.Invoke(Run1, Run2);
}
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抛出的异常");
}
}
<3> 并行计算中我可以留一个硬件线程出来吗?
默认的情况下,底层机制会尽可能多的使用硬件线程,然而我们使用手动指定的好处是我们可以在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);
}
}