相关文章:4.0中的
并行计算和多线程详解(一) 多线程部分
多线程在4.0中被简化了很多,仅仅只需要用到System.Threading.Tasks.::.Task类,下面就来详细介绍下Task类的使用。
一、简单使用
开启一个线程,执行循环方法,返回结果。开始线程为Start(),等待线程结束为Wait()。
Code /// <summary> /// Task简单使用 /// </summary> private void Demo1() { int i = 0; Random r = new Random(DateTime.Now.Second); Task t = new Task(() => { for (int v = 0; v < 100; v++) i += r.Next(100); }); t.Start(); t.Wait();
Console.WriteLine("这是执行Task1后等待完成:" + i.ToString()); Console.ReadLine(); }
比以前使用Thread方便多了吧。上面的例子是使用外部的变量获得结果,下面的例子是用Task<T>直接返回结果,当调用Result属性时,会自动等待线程结束,等同调用了Wait()。代码如下:
Code /// <summary> /// Task带返回值 /// </summary> private void Demo2() { Random r = new Random(DateTime.Now.Second); Task<int> t = new Task<int>(() => { int i = 0; for (int v = 0; v < 100; v++) i += r.Next(100); return i; }); t.Start(); Console.WriteLine("这是执行Task1
获取返回值:" + t.Result.ToString()); Console.ReadLine(); }
总结1:Task的使用比Thread简单很多,减少了同步,等待等等问题,唯一的遗憾是不支持Thread的IsBackground。
结论1:如果不需要使用IsBackground,那么尽情的使用Task吧。
二、线程执行完毕后调用另一个线程
也就是两个线程,有序的执行,这里使用ContinueWith(),t执行完毕后再执行一个task方法,不多说了代码如下:
Code /// <summary> /// Task 执行完毕后调用另一个Task /// </summary> private void Demo3() { Random r = new Random(DateTime.Now.Second); Task<int> t = new Task<int>(() => { int i = 0; for (int v = 0; v < 100; v++) i += r.Next(100); return i; }); t.ContinueWith((Task<int> task) => { Console.WriteLine("这是执行完毕Task1后继续调用Task2:" + task.Result.ToString()); }); t.Start(); Console.ReadLine(); }
也可以直接链式的写下去,代码如下:
Code /// <summary> /// Task 执行完毕后调用另一个Task(链式写法) /// </summary> private void Demo4() { Random r = new Random(DateTime.Now.Second); Task<int> t = new Task<int>(() => { int i = 0; for (int v = 0; v < 100; v++) i += r.Next(100); return i; }); Task t2 = t.ContinueWith((Task<int> task) => { Console.WriteLine(task.Result.ToString()); }); t2.ContinueWith(task => { Console.WriteLine("这是执行完毕Task1后继续调用Task2,Task2后调用Task3。"); }); t.Start(); Console.ReadLine(); }
结论2:Task可以便捷的将几个方法串行执行。
三、带有父子关系的线程/多线程并行开启
t带有t1,t2,t3三个子线程,执行t的时候t1,t2,t3可并行处理,t必须等待t1,t2,t3都执行完毕后才能结束。创建子Task时候必须指定参数为AttachedToParent。
Code /// <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<int> t = new Task<int>(() => { Task<int> t1 = new Task<int>(() => { int i = 0; Random r = new Random(DateTime.Now.Second); for (int v = 0; v < 100; v++) i += r.Next(100); return i; }, TaskCreationOptions.AttachedToParent); Task<int> t2 = new Task<int>(() => { int i = 0; Random r = new Random(DateTime.Now.Second); for (int v = 0; v < 100; v++) i += r.Next(100); return i; }, TaskCreationOptions.AttachedToParent); Task<int> t3 = new Task<int>(() => { int i = 0; Random r = new Random(DateTime.Now.Second); for (int v = 0; v < 100; v++) i += r.Next(100); return i; }, TaskCreationOptions.AttachedToParent); t1.Start(); t2.Start(); t3.Start(); return t1.Result + t2.Result + t3.Result; }); t.Start(); t.Wait(); Console.WriteLine("这是带有父子关系的Task集合:" + t.Result.ToString()); Console.ReadLine(); }
结论3:多个线程的同时开启在这里也很方便,也不用担心同步等问题。
四、Task的中断
这个很复杂,就不多说了,代码中有比较详细的介绍。
Code /// <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() { CancellationTokenSource cts = new CancellationTokenSource(); var c
token = cts.Token; Task t1 = new Task(() => { for (int v = 0; v < 10; v++) { if (ctoken.IsCancellationRequested) { //第一种写法