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 如此操作,基本满足需求

 

posted @ 2020-10-26 12:00  郎中令  阅读(347)  评论(0编辑  收藏  举报