c#多线程之Task

需求:比如说开始一个项目,首先拆分模块交给不同的人干,全部干完之后进行整合测试,项目完结。其中拆分模块之后每个人都做不同的工作,这是完全可以并行操作的,所以可以使用多线程,比如:

代码:

        /// <summary>
        /// Task专题解析
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTask_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****** **btnTask_Click   异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************");
            Console.WriteLine("项目开始");
            Console.WriteLine("拆分模块");
            Console.WriteLine("指定开发人员开发对应模块");
            Task.Run(() => this.Coding("张三", "Website"));
            Task.Run(() => this.Coding("李四", "WebApi")); 
            Console.WriteLine("整合测试");
            Console.WriteLine("项目结束");
            Console.WriteLine($"**** *****btnTask_Click   异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************");

        }
        /// <summary>
        /// 编码模拟
        /// </summary>
        /// <param name="name"></param>
        /// <param name="project"></param>
        private void Coding(string name, string project)
        {
            Console.WriteLine($"***** *********Coding  start {name}- {project}-{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}** ***********");
            long IResult = 0;
            for (int i = 0; i < 1000000000; i++)
            {
                IResult = i;
            }
            Console.WriteLine($"***** ***********Coding end {name}- {project}-{Thread.CurrentThread.ManagedThreadId.ToString()}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}** **********");

        }

结果:

****** **btnTask_Click   异步方法 start 1-2021-09-17 18:21:07*************************
项目开始
拆分模块
指定开发人员开发对应模块
整合测试
项目结束
**** *****btnTask_Click   异步方法 end 1 -2021-09-17 18:21:07 *************************
**************************Coding  start 张三- Website-4-2021-09-17 18:21:07*************************
**************************Coding  start 李四- WebApi-7-2021-09-17 18:21:07*************************
**************************Coding end 李四- WebApi-7-2021-09-17 18:21:09*************************
**************************Coding end 张三- Website-4-2021-09-17 18:21:09*************************

 从这个结果可以看出,不符合实际情况,因为之后模块全部开发完成之后才能项目结束。所以改成下面代码:

 /// <summary>
        /// Task专题解析
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTask_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****** **btnTask_Click   异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************");
            Console.WriteLine("项目开始");
            Console.WriteLine("拆分模块");
            Console.WriteLine("指定开发人员开发对应模块");
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => this.Coding("张三", "Website")));
            taskList.Add(Task.Run(() => this.Coding("李四", "WebApi")));

            // 等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程。
            Task.WaitAll(taskList.ToArray()); 
            Console.WriteLine("整合测试");
            Console.WriteLine("项目结束");
            Console.WriteLine($"**** *****btnTask_Click   异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************");

        }

结果:

****** **btnTask_Click   异步方法 start 1-2021-09-17 18:26:19*************************
项目开始
拆分模块
指定开发人员开发对应模块
*** **Coding  start 张三- Website-3-2021-09-17 18:26:19***** ***
*** **Coding  start 李四- WebApi-4-2021-09-17 18:26:19***** ***
*** ****Coding end 李四- WebApi-4-2021-09-17 18:26:22********** *
*** ****Coding end 张三- Website-3-2021-09-17 18:26:22********** *
整合测试
项目结束
**** *****btnTask_Click   异步方法 end 1 -2021-09-17 18:26:22 *************************

还有一种方法WaitAny,只要传入参数中的任何一个线程结束了,就可以再继续往下执行。

     private void btnTask_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****** **btnTask_Click   异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************");
            Console.WriteLine("项目开始");
            Console.WriteLine("拆分模块");
            Console.WriteLine("指定开发人员开发对应模块");
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => this.Coding("张三", "Website")));
            taskList.Add(Task.Run(() => this.Coding("李四", "WebApi")));
            //只有taskList中有一个线程执行完毕就不会再阻塞当前主线程,使其继续往下执行
            Task.WaitAny(taskList.ToArray());
            Console.WriteLine("项目开发里程碑达成");
            // 等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程。
            Task.WaitAll(taskList.ToArray());
            Console.WriteLine("整合测试");
            Console.WriteLine("项目结束");
            Console.WriteLine($"**** *****btnTask_Click   异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************");

        }

结果:李四率先完成,所以可以接着向下执行。

****** **btnTask_Click   异步方法 start 1-2021-09-17 18:35:30*************************
项目开始
拆分模块
指定开发人员开发对应模块
*** **Coding  start 张三- Website-3-2021-09-17 18:35:30***** ***
*** **Coding  start 李四- WebApi-4-2021-09-17 18:35:30***** ***
*** ****Coding end 李四- WebApi-4-2021-09-17 18:35:32********** *
项目开发里程碑达成
*** ****Coding end 张三- Website-3-2021-09-17 18:35:32********** *
整合测试
项目结束
**** *****btnTask_Click   异步方法 end 1 -2021-09-17 18:35:32 *************************

但是上面这2种方法都是阻塞的主线程,所以当前的界面是不能再响应其他操作(只有主线程才能响应界面操作),比如无法拖动,点击等。

可以使用TaskFactory类来解决:

        /// <summary>
        /// Task专题解析
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTask_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****** **btnTask_Click   异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************");
            Console.WriteLine("项目开始");
            Console.WriteLine("拆分模块");
            Console.WriteLine("指定开发人员开发对应模块");
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => this.Coding("张三", "Website")));
            taskList.Add(Task.Run(() => this.Coding("李四", "WebApi")));
            TaskFactory taskFactory = new TaskFactory();
            //任一线程执行完毕之后取消主线程的阻塞
            taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
            {
                Console.WriteLine($"{t.AsyncState}第一个完成");
            });
            //全部线程执行完毕之后取消主线程的阻塞
            taskFactory.ContinueWhenAll(taskList.ToArray(), tArr =>
            {
                Console.WriteLine($"整合测试"); 
            });


                Task.WaitAll(taskList.ToArray());  

                Console.WriteLine($"项目完成");

            Console.WriteLine($"**** *****btnTask_Click   异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************");

        }

结果:

****** **btnTask_Click   异步方法 start 1-2021-09-17 19:04:28*************************
项目开始
拆分模块
指定开发人员开发对应模块
项目完成
**** *****btnTask_Click   异步方法 end 1 -2021-09-17 19:04:28 *************************
*** **Coding  start 李四- WebApi-7-2021-09-17 19:04:29***** ***
*** **Coding  start 张三- Website-6-2021-09-17 19:04:29***** ***
*** ****Coding end 李四- WebApi-7-2021-09-17 19:04:31********** *
第一个完成
*** ****Coding end 张三- Website-6-2021-09-17 19:04:31********** *
整合测试

但是由于多线程是无序性的,看上面的结果,项目完成这句话的输出跑前面去了,比项目测试还早,按照业务逻辑来说只能先测试没问题了才能宣告项目的结束。 

     private void btnTask_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"****** **btnTask_Click   异步方法 start {Thread.CurrentThread.ManagedThreadId}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}*************************");
            Console.WriteLine("项目开始");
            Console.WriteLine("拆分模块");
            Console.WriteLine("指定开发人员开发对应模块");
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => this.Coding("张三", "Website")));
            taskList.Add(Task.Run(() => this.Coding("李四", "WebApi")));
            TaskFactory taskFactory = new TaskFactory();
            //任一线程执行完毕之后取消主线程的阻塞
            taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
            {
                Console.WriteLine($"{t.AsyncState}第一个完成");
            });
            //改成下面这种形式,因为ContinueWhenAll方法的返回值是一个Task,这样的话在下面Task.WaitAll(taskList.ToArray());中就可以阻塞所有的子线程了,就能够按照顺序执行
            taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tArr =>
           {
               Console.WriteLine($"整合测试");
           }));

            Task.WaitAll(taskList.ToArray());
            Console.WriteLine($"项目完成");
            Console.WriteLine($"**** *****btnTask_Click   异步方法 end {Thread.CurrentThread.ManagedThreadId} -{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} *************************");

        }

结果:

****** **btnTask_Click   异步方法 start 1-2021-09-17 19:43:26*************************
项目开始
拆分模块
指定开发人员开发对应模块
*** **Coding  start 张三- Website-3-2021-09-17 19:43:26***** ***
*** **Coding  start 李四- WebApi-4-2021-09-17 19:43:26***** ***
*** ****Coding end 张三- Website-3-2021-09-17 19:43:28********** *
第一个完成
*** ****Coding end 李四- WebApi-4-2021-09-17 19:43:28********** *
整合测试
项目完成
**** *****btnTask_Click   异步方法 end 1 -2021-09-17 19:43:28 *************************
原文地址:https://www.cnblogs.com/anjingdian/p/15304192.html