Task多线程调度常用操作
当操纵大批量数据时,或者比较耗时的请求时,往往需要线程后台操作,而有时候单个线程无法满足需求,就需要多个线程进行协调操作,根据线程之间的联系,进行不同的任务调度。或排序,或并列。常用的线程操作从一开始的 Thread 到 net4.0的 Task,再到后来net 4.5的 async/await,各有千秋,一般我是Task用顺手了,基于线程池队列的操作,参数比较多,对待不同的业务场景基本都能满足,相对 async/await 用的不是太多
//Task的阻塞方法(Wait/WaitAll/WaitAny) //Task的延续操作(WhenAny/WhenAll/ContinueWith) //Task的任务取消(CancellationTokenSource)
如: 遍历执行某个集合,每个实体下都要进行大量耗时工作。而上一个遍历未完整之前,无法操作下一个任务。听起来很绕,举个简单的应用场景,遍历Excel表,表有三个包含类似结构数据sheet,依次将sheet中的数据取出来,再进行一系列的构造,存入数据表中,由于数据量过大,操作耗时,写入数据库时会有锁表的情况发生,若多线程并列操纵,锁表时会造成其他线程无法进一步写入数据。这里就需要对线程进行调度,只有当上一个线程完成时,下一个线程才开始。设计关键字 ContinueWith,使用时需要传入上一个task,因为程序需要了解到上一个线程的状态,只有完成时,才会安排当前任务。
模拟过程:
//业务逻辑 TTT(index); //index =0 则 var t1 = Task.Factory.StartNew(() => { TTT(null, index); }) //index =1 则 var t2 = t1.ContinueWith(item => { TTT(t1, index); }); //index =2 则 var t3= t2.ContinueWith(item => { TTT(t2, index); }); //index =3 则 var t4= t2.ContinueWith(item => { TTT(t3, index); }); //..... //index无论是多少,都要依次执行前面的任务
于是,根据模拟的过程来代码的编写 (PS:最开始想的是递归,深入一想,递归一般是后面的叠加前面的,我这里是依次执行,一时半会没想到合适的递归方法)
static void Main(string[] args) { int i = 0; int n = 3; //sheet表 个数 var taskf = (Task)null; Task.Factory.StartNew(()=> { while (i < n) { //此行很重要,构造每个线程内自己的变量,否则,所有线程的变量是相同的 var index = i; if (i == 0) { //第一个任务时,无上一个任务,故传入null taskf = Task.Factory.StartNew(() => { TTT(null, index); } ); } if (i > 0) { //传入上一个任务作为参数,并将任务结果赋值 taskf = taskf.ContinueWith(item => { TTT(taskf, index); }); } i = i + 1; } //等待所有线程完成 taskf.Wait(); Console.WriteLine("线程全部执行完成"); }); Console.WriteLine("我是主函数结果"); Console.ReadKey(); } public static void TTT(Task t,int x) { //模拟相关逻辑 Console.WriteLine("我是第{0}个Task, 开始时间:" + DateTime.Now, x); Thread.Sleep(5000); Console.WriteLine("第{0}Task 结束,结束时间:" + DateTime.Now, x); }
如上代码,满足需求,异步执行任务,多线程内,线程依次执行,结果如下
有时候,线程需要同时操作,相互之间的结果不存在依赖关系,这里就没必要进行等待其他数据了,这时候只需要判断当前业务是需要同步等待返回结果,还是异步等待结果,如果是异步,代码如下:
static void Main(string[] args) { Console.WriteLine("主程序入口开始..."); //初始化任务集合 var tasks = new List<Task>(); Task.Factory.StartNew(()=> { //依次写入任务 tasks.Add(Task.Factory.StartNew(() => { TTT(0); })); tasks.Add(Task.Factory.StartNew(() => { TTT(1); })); tasks.Add(Task.Factory.StartNew(() => { TTT(2); })); //等待当前任务集合全部执行完成 Task.WaitAll(tasks.ToArray()); Console.WriteLine(); Console.WriteLine("所有线程全部完成"); }); Console.WriteLine("主程序结束"); Console.ReadKey(); } public static void TTT(int index) { Console.WriteLine(" 第{0}个任务,开始时间:" + DateTime.Now, index); Thread.Sleep(3000); Console.WriteLine("我是第{0}个任务,结束时间:"+DateTime.Now,index); }
当需要同步线程结果,等待所有线程结束后,函数才返回时,只需要 Task.Factory.StartNew 提出来,直接构造任务集合即可
static void Main(string[] args) { Console.WriteLine("主程序入口开始..."); //初始化任务集合 var tasks = new List<Task> { //依次写入任务 Task.Factory.StartNew(() => { TTT(0); }), Task.Factory.StartNew(() => { TTT(1); }), Task.Factory.StartNew(() => { TTT(2); }) }; //等待当前任务集合全部执行完成 Task.WaitAll(tasks.ToArray()); Console.WriteLine(); Console.WriteLine("所有线程全部完成"); Console.WriteLine("主程序结束"); Console.ReadKey(); } public static void TTT(int index) { Console.WriteLine(" 第{0}个任务,开始时间:" + DateTime.Now, index); Thread.Sleep(3000); Console.WriteLine("我是第{0}个任务,结束时间:"+DateTime.Now,index); }
到这里,针对一些常见的业务,使用Task 如此操作,基本满足需求