资料: 4.0中的并行计算和多线程详解(二) MSDN:任务并行(任务并行库) ///引用 using System.Threading.Tasks; #region 隐式创建和运行任务 /// <summary> /// 同时运行DoComputation1和DoComputation2 /// DoComputation1方法耗时2秒 /// DoComputation2方法耗时7秒 /// </summary> void Parallelmethod() { Console.WriteLine("{0} Parallel.Invoke Begin", DateTime.Now.ToString("mm:ss fff ")); Parallel.Invoke(() => DoComputation1(), () => DoComputation2()); Console.WriteLine("{0} Parallel.Invoke Finish ", DateTime.Now.ToString("mm:ss fff ")); ///Output: //18:32 436 Parallel.Invoke Begin //[1] 18:34 452 DoComputation1 done //[1] 18:39 452 DoComputation2 done //18:39 452 Parallel.Invoke Finish } #endregion /* * 总结1:Task的使用比Thread简单很多,减少了同步,等待等等问题,唯一的遗憾是不支持Thread的IsBackground。 * 结论1:如果不需要使用IsBackground,那么尽情的使用Task吧。 */ #region 简单的构造一个任务 /// <summary> /// 简单的构造一个任务。在创建任务时,您赋予它一个用户委托,该委托封装该任务将执行的代码。 /// 该委托可以表示为命名的委托、匿名方法或 lambda 表达式。 lambda 表达式可以包含对命名方法的调用 /// 显式创建和运行任务 /// </summary> void taskmethod1() { // Create a task and supply a user delegate by using a lambda expression. var taskA = new Task( () => { Console.WriteLine(DateTime.Now.ToString("mm:ss fff ") + "new Task"); Console.WriteLine( DateTime.Now.ToString("mm:ss fff ")+"Hello from taskA."); } ); // Start the task. taskA.Start(); Console.WriteLine(DateTime.Now.ToString("mm:ss fff ") + "taskA.Start()"); // Output a message from the calling thread. Console.WriteLine("Hello from the calling thread."); /* Output: * 44:57 624 taskA.Start() * Hello from the calling thread. * 44:57 624 new Task * 44:57 655 Hello from taskA. * 44:57 624 taskA.Start() * Hello from the calling thread. * 44:57 624 new Task * 44:57 655 Hello from taskA. */ } #endregion #region 创建多个任务,分别执行 /// <summary> /// 创建多个任务,分别执行 /// </summary> void taskmethod2() { Task<double>[] taskArray = new Task<double>[] { Task<double>.Factory.StartNew(() => DoComputation1()), // May be written more conveniently like this: Task.Factory.StartNew(() => DoComputation2()), Task.Factory.StartNew(() => DoComputation3()) }; Console.WriteLine("{0} taskArray[] Created", DateTime.Now.ToString("mm:ss fff ")); //double[] results = new double[taskArray.Length]; //for (int i = 0; i < taskArray.Length; i++) //{ // results[i] = taskArray[i].Result; // Console.WriteLine("{0} for i={1} finish", DateTime.Now.ToString("mm:ss fff "),i); //} } double DoComputation1() { System.Threading.Thread.Sleep(2000); Console.WriteLine("[1] {0} DoComputation1 done", DateTime.Now.ToString("mm:ss fff ")); return 1; } double DoComputation2() { System.Threading.Thread.Sleep(7000); Console.WriteLine("[1] {0} DoComputation2 done", DateTime.Now.ToString("mm:ss fff ")); return 2; } double DoComputation3() { System.Threading.Thread.Sleep(9000); Console.WriteLine("[1] {0} DoComputation3 done", DateTime.Now.ToString("mm:ss fff ")); return 3; } #endregion #region 创建含有TaskCreationOptions 参数的任务 /// <summary> /// 含有TaskCreationOptions 参数的任务.可以通过按位 OR 运算组合选项。 下面的示例演示一个具有 LongRunning 和 PreferFairness 选项的任务。 /// PreferFairness:指定应当计划任务,以使越早创建的任务将更可能越早执行,而越晚创建的任务将更可能越晚执行。 /// LongRunning:指定该任务表示长时间运行的操作。 /// AttachedToParent :指定应将任务创建为当前任务(如果存在)的附加子级 /// </summary> void taskmethod3() { var task3 = new Task(() => MyLongRunningMethod(), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness); task3.Start(); Console.WriteLine("{0} task3.Start()", DateTime.Now.ToString("mm:ss fff ")); } void MyLongRunningMethod() { System.Threading.Thread.Sleep(9000); Console.WriteLine("{0} DoComputation1 done", DateTime.Now.ToString("mm:ss fff ")); } #endregion #region 延续的任务 /// <summary> /// 延续的任务 /// [1] DoComputation1、DoComputation2、DoComputation3全部完成后执行getDataInsert1 /// [1] DoComputation1、DoComputation2、DoComputation3有任意一个完成后执行getDataInsert2 /// [2] GetFileData完成后执行Analyze完成后执行Summarize /// </summary> void taskmethod4() { Task<double>[] taskArray = new Task<double>[] { Task<double>.Factory.StartNew(() => DoComputation1()), // May be written more conveniently like this: Task.Factory.StartNew(() => DoComputation2()), Task.Factory.StartNew(() => DoComputation3()) }; Console.WriteLine("[1] {0} taskArray[] Created", DateTime.Now.ToString("mm:ss fff ")); Task getDataInsert1 = Task.Factory.ContinueWhenAll<double>( new Task<double>[] { taskArray[0], taskArray[1], taskArray[2] }, (Task<double>[] task) => GetFileDataInsert1()); Console.WriteLine("[1] {0} getDataInsert1 Created", DateTime.Now.ToString("mm:ss fff ")); Task getDataInsert2 = Task.Factory.ContinueWhenAny<double>( new Task<double>[] { taskArray[0], taskArray[1], taskArray[2] }, (Task<double> task) => GetFileDataInsert2()); Console.WriteLine("[1] {0} getDataInsert2 Created", DateTime.Now.ToString("mm:ss fff ")); Task<byte[]> getData = new Task<byte[]>(() => GetFileData()); Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result)); Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result)); getData.Start(); Console.WriteLine("[2] {0} getData.Start()", DateTime.Now.ToString("mm:ss fff ")); //The same result.code or... //Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData()) // .ContinueWith((x) => Analyze(x.Result)) // .ContinueWith((y) => Summarize(y.Result)); //Console.WriteLine("{0} reportData2 build", DateTime.Now.ToString("mm:ss fff ")); Console.WriteLine("[2] reportData.Result is /"{0}/"", reportData.Result); } byte[] GetFileData() { System.Threading.Thread.Sleep(1000); Console.WriteLine("[2] {0} GetFileData finish", DateTime.Now.ToString("mm:ss fff ")); return new byte[] { }; } byte[] GetFileDataInsert1() { System.Threading.Thread.Sleep(4000); Console.WriteLine("[1] {0} GetFileDataInsert1 finish", DateTime.Now.ToString("mm:ss fff ")); return new byte[] { }; } byte[] GetFileDataInsert2() { System.Threading.Thread.Sleep(3000); Console.WriteLine("[1] {0} GetFileDataInsert2 finish", DateTime.Now.ToString("mm:ss fff ")); return new byte[] { }; } double[] Analyze(object o) { System.Threading.Thread.Sleep(2000); Console.WriteLine("[2] {0} Analyze finish", DateTime.Now.ToString("mm:ss fff ")); return new double[] { }; } string Summarize(object o) { System.Threading.Thread.Sleep(4000); Console.WriteLine("[2] {0} Summarize finish", DateTime.Now.ToString("mm:ss fff ")); return ""; } #endregion #region 父子关系的Task集合 /// <summary> /// 带有父子关系的Task集合,[TaskCreationOptions.AttachedToParent] /// /// 值 说明 /// None 默认值,此Task会被排入Local Queue中等待执行,采用LIFO模式。 /// AttachedToParent 建立的Task必须是外围的Task的子Task,也是放入Local Queue,採LIFO模式。 /// LongRunning 建立的Task不受Thread Pool所管理,直接新增一个Thread来执行此Task,无等待、无排程。 /// PreferFairness 建立的Task直接放入Global Queue中,採FIFO模式。(比上面的优先级低) /// </summary> private void Demo5() { Task<double> t = new Task<double>(() => { Task<double> t1 = new Task<double>(() => DoComputation1(), TaskCreationOptions.AttachedToParent); Task<double> t2 = new Task<double>(() => DoComputation2(), TaskCreationOptions.AttachedToParent); Task<double> t3 = new Task<double>(() => DoComputation3(), TaskCreationOptions.AttachedToParent); t1.Start(); t2.Start(); t3.Start(); return t1.Result + t2.Result + t3.Result; }); t.Start(); Console.WriteLine("{0} t.Start()", DateTime.Now.ToString("mm:ss fff ")); t.Wait(); Console.WriteLine("这是带有父子关系的Task集合:" + t.Result.ToString()); } #endregion #region 创建分离的嵌套任务 /// <summary> /// 如果在一个任务中运行的用户代码创建一个新任务,且未指定 AttachedToParent 选项, /// 则该新任务不采用任何特殊方式与外部任务同步。 此类任务称为“分离的嵌套任务”。 /// 下面的示例演示一个任务,该任务创建一个分离的嵌套任务 /// </summary> void taskmethod5() { var outer = Task.Factory.StartNew(() => { Console.WriteLine("Outer task beginning."); var child = Task.Factory.StartNew(() => { System.Threading.Thread.SpinWait(5000000); Console.WriteLine("Detached task completed."); }); }); outer.Wait(); Console.WriteLine("Outer task completed."); } #endregion #region 等待任务(组) /// <summary> /// System.Threading.Tasks.Task 类型和 System.Threading.Tasks.Task<TResult> 类型提供使您可以等待任务完成的 Wait 方法的一些重载。 /// 此外,静态 TaskWaitAll() 方法和 TaskWaitAny() 方法的重载使您可以等待任务数组的任一任务或所有任务完成。 ///通常,会出于以下某个原因等待任务: ///主线程依赖于任务计算的最终结果。 ///您必须处理可能从任务引发的异常。 ///下面的示例演示不包含异常处理的基本模式。 /// </summary> void taskmethod6() { Task[] tasks = new Task[3] { Task.Factory.StartNew(() => MethodA()), Task.Factory.StartNew(() => MethodB()), Task.Factory.StartNew(() => MethodC()) }; Task.WaitAll(tasks); } void MethodA() { } void MethodB() { } void MethodC() { } #endregion #region Task的中断,中断很复杂,但是对一般逻辑来说,是没有很大必要的。 /// <summary> /// 中途取消Task执行,Token /// /// 一是正常结束、二是产生例外、三是透过Cancel机制,这三种情况都会反映在Task.Status属性上 /// 值 说明 /// Created Task已经建立,但未呼叫Start。 /// WaitingForActivation Task已排入排程,但尚未执行(一般我们建立的Task不会有此状态,只有ContinueWith所产生的Task才会有此状态)。 /// WaitingToRun Task已排入排程,等待执行中。 /// Running Task执行中。 /// WaitingForChildrenToComplete Task正等待子Task結束。 /// RanToCompletion Task已经正常执行完毕。 /// Canceled Task已被取消。 /// Faulted Task执行中发生未预期例外。 /// /// 除了Status属性外,Task还提供了另外三个属性来判定Task状态。 /// 属性 说明 /// IsCompleted Task已经正常执行完毕。 /// IsFaulted Task执行中法生未预期例外。 /// IsCanceled Task已被取消。 /// </summary> private void Demo6() { System.Threading.CancellationTokenSource cts = new System.Threading.CancellationTokenSource(); var ctoken = cts.Token; Task t1 = new Task(() => { for (int v = 0; v < 10; v++) { if (ctoken.IsCancellationRequested) { //第一种写法 //这个会抛出异常 ctoken.ThrowIfCancellationRequested(); //另一种写法 //这个不会返回异常,但是获取不到是否是中断还是执行完毕。 //return; } System.Threading.Thread.Sleep(1000); Console.WriteLine(v); } }, ctoken); t1.Start(); System.Threading.Thread.Sleep(2000); cts.Cancel(); try { t1.Wait(); } catch { if (t1.IsCanceled) Console.WriteLine("cancel"); } Console.ReadLine(); cts.Dispose(); } #endregion