所谓并行程序开发是我们不关系任务什么时候执行,只关心怎么利用计算机资源更快的执行。
并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴。所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务,这些任务不考虑互相的依赖和顺序。
NET Framework4.0引入了Task Parallel Library(TPL)实现了基于任务设计而不用处理重复复杂的线程的并行开发框架。它支持数据并行,任务并行与流水线。核心主要是Task,但是一般简单的并行我们可以利用Parallel提供的静态类如下三个方法。
Parallel.Invoke 对给定任务实现并行开发
Parallel.For 对固定数目的任务提供循环迭代并行开发
parallel.Foreach 对固定数目的任务提供循环迭代并行开发
注意:所有的并行开发不是简单的以为只要将For或者Foreach换成Parallel.For与Parallel.Foreach这样简单。
1 For, Foreach和Parallel.For、Parallel.Foreach
1.1 基本分析
for与foreach
相对于原来的for语句foreach具有更好的执行效率
for与Parallel.For区别:
从CPU使用方面而言,Parallel.For 属于多线程范畴,可以开辟多个线程使用CPU内核,也就是说可以并行处理程序。For 循环是单线程的,一个线程执行完所有循环。也就说For是同步,Parallel.For 是异步执行。
任务的开销大小对并行任务的影响:
如果任务很小,那么由于并行管理的附加开销(任务分配,调度,同步等成本),可能导致并行执行并不是最优化方案。
Parallel.ForEach 和 ForEach 与 Parallel.For 和 For 一样,一个是异步执行,开辟多个线程。一个是同步执行,开辟一个线程。因此,效率方面同上,主要看执行的什么任务。
Parallel.For不能添加ref或out修饰的变量,错误提示为:不能在匿名表达式、lanmda表达式或查询表达式内使用ref或out变量
停止并行For循环的方法:
在 Parallel.For 或 Parallel.ForEach 循环中,不能使用与顺序循环中相同的 break 或 Exit 语句,这是因为这些语言构造对于循环是有效的,而并行“循环”实际上是方法,不是循环。 相反,可以使用 Stop 或 Break 方法。 Parallel.For 的一些重载接受 Action<int, ParallelLoopState>作为输入参数。 ParallelLoopState 对象由运行时在后台创建,你可以在 lambda 表达式中为它指定你喜欢的任何名称。
在 Action<int, ParallelLoopState>的委托方法中调用ParallelLoopState参数的Stop或Break方法。
Stop 方法;它将告知循环的所有迭代(包括那些在其他线程上的当前迭代之前开始的迭代)在方便的情况下尽快停止。
Break 方法;它会导致其他线程放弃对后续片段的工作(如果它们正忙于任何这样的工作),并在退出循环之前处理完所有前面的元素。
区别就在于,Stop仅仅通知其他迭代尽快结束,而Break不仅通知其他迭代尽快结束,同时还要保证退出之前要完成LowestBreakIteration之前的迭代。 例如,对于从 0 到 1000 并行迭代的 for 循环,如果从第 100 此迭代开始调用 Break,则低于 100 的所有迭代仍会运行,从 101 到 1000 的迭代则不必要。而调用Stop方法不保证低于 100 的所有迭代都会运行。
1.2 示例代码
public static void test() { Parallel.For(1, 5, x=> { Console.WriteLine(x); }); Console.WriteLine("------------------------"); var numbers = Enumerable.Range(103, 8); Parallel.ForEach(numbers, x => { Console.WriteLine(x); }); Console.WriteLine("------------------------"); Parallel.Invoke( ()=>{ Console.WriteLine("ABC");}, ()=>{ Console.WriteLine("xxxOMG");}, ()=>{ Console.WriteLine("MMG");}, ()=>{ Console.WriteLine("oooMMG");}, () => { Console.WriteLine("999ABC"); }, ()=>{ Console.WriteLine("888MMG");} ); }
运行结果截图:
2 Parallel.Invoke
两个重载方法:
public static void Invoke(params Action[] actions); public static void Invoke(ParallelOptions parallelOptions, params Action[] actions);
2.1 action调用模式及示例
2.1.1 调用方法解释
//方式一 Parallel.Invoke(() => Task1(), () => Task2(), () => Task3()); //方式二 Parallel.Invoke(Task1, Task2, Task3); //方式三 Parallel.Invoke( () => { Task1(); }, Task2, delegate () { Task3(); console.write('do someting!');});
2.1.2 示例代码
public class ParallelInvoke { /// <summary> /// Invoke方式一 action /// </summary> public void Client1() { Stopwatch stopWatch = new Stopwatch(); Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId); stopWatch.Start(); Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3")); stopWatch.Stop(); Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds); } private void Task1(string data) { Thread.Sleep(5000); Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId); } private void Task2(string data) { Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId); } private void Task3(string data) { Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId); } }
调用:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using System.Threading; namespace myParallel { class Program { static void Main(string[] args) { ParallelInvoke pi = new ParallelInvoke(); pi.Client1(); } } }

