异步多线程 2 Task

Task

  task可以说是多线程的最佳实现:

    1.task用的是线程池的线程,节省了资源,不用像Thread那样麻烦;

    2.封装了丰富的方法,很方便。

 

  创建task对象的方式有很多,这里介绍几个常用的:

  用Start方法启动无参数无返回值的委托

  直接new实例化Task对象并调用Start方法。

        private static void AsyncMethod3()
        {
            Console.WriteLine("AsyncMethod3开始线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Action action = DoSomething;
            Task task = new Task(action);
            Console.WriteLine("AsyncMethod3开始线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
            task.Start();

        }

        private static void DoSomething(string name)
        {
            Console.WriteLine("开始线程id:" + Thread.CurrentThread.ManagedThreadId.ToString() + " name:" + name);
            long result = 0;
            for (long i = 0; i < 1000000000; i++)
            {
                result += i;
            }
            Console.WriteLine("结束线程id:" + Thread.CurrentThread.ManagedThreadId.ToString() + " name:" + name);

        }     
        private static void DoSomething()
        {
            DoSomething("我是默认姓名");
        }   

 

 看,跟之前用委托的BeginInvoke效果一样。

 

用Run方法创建并启动,有参数无返回值的委托

  这里用Run启动3个线程

private static void AsyncMethod3()
        {
            Console.WriteLine("AsyncMethod3开始线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Task.Run(() => DoSomething("张三"));
            Task.Run(() => DoSomething("李四"));
            Task.Run(() => DoSomething("王五"));
            Console.WriteLine("AsyncMethod3开始线程:" + Thread.CurrentThread.ManagedThreadId.ToString());

        }

结果

  

 

 

等待方法WaitAll,全部等待

  有时候多个业务可以多线程执行,但是需要等他们全部执行完成后再返回或通知,可以用WaitAll方法,阻塞当前线程,直到所有任务结束再执行下一行。

  改一下上例的方法

            List<Task> taskList = new List<Task>() {
                Task.Run(() => DoSomething("张三")),
                Task.Run(() => DoSomething("李四")),
                Task.Run(() => DoSomething("王五"))
            };
            Task.WaitAll(taskList.ToArray());

结果

 

 

 

等待方法WaitAny,多个task完成任何一个就执行下面的代码

Task.WaitAny(taskList.ToArray());

结果

 

 

但是无论是WatiAll还是WatiAny,都有一个问题,那就是会阻塞主线程,所以这里再介绍一个新的开启线程的方法TaskFactory

 

TaskFactory

  ContinueWhenAll:等待全部任务完成后,开启一个新task继续后续任务,不会阻塞主线程,其实就是集体回调的效果

    TaskFactory taskFactory = new TaskFactory();
            //等待全部任务完成后,启动一个新的task来完成后续任务
            taskFactory.ContinueWhenAll(taskList.ToArray(), atArray =>
             {
                 Console.WriteLine("AsyncMethod3的TaskFactory.ContinueWhenAll线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
             });

结果

 

 

  ContinueWhenAny:任何一个线程任务完成后,开启一个新task来完成后续任务,不会阻塞主线程

        taskFactory.ContinueWhenAny(taskList.ToArray(), atArray =>
            {
                Console.WriteLine("AsyncMethod3的TaskFactory.ContinueWhenAny线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
            });    

 

 

WaitAll,WaitAny,ContinueWhenAll,ContinueWhenAny 熟练掌握着4种,基本也就能处理大部分的多线程业务了,注意的是后两种要配合TaskFactory。

 

回调,单个task的回调 ContinueWith

       private static void AsyncMethod3()
        {
            Console.WriteLine("AsyncMethod3开始线程:" + Thread.CurrentThread.ManagedThreadId.ToString());


            #region 回调
            Task task = Task.Run(() =>
            {
                DoSomething("张三");
            });
            task.ContinueWith(t =>
            {
                Console.WriteLine("Task回调ContinueWith线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
            });
            #endregion

            Console.WriteLine("AsyncMethod3结束线程:" + Thread.CurrentThread.ManagedThreadId.ToString());

        }

回调的线程可能是已有线程3,也可能是新的线程4,这里是不确定的

 

 

 

返回值,其实用的是Func

        private static void AsyncMethod3()
        {
            Console.WriteLine("AsyncMethod3开始线程:" + Thread.CurrentThread.ManagedThreadId.ToString());

            #region 返回值
            var r = Task.Run<string>(() => GetApi("张三"));
            Console.WriteLine(r.Result);
            #endregion

            Console.WriteLine("AsyncMethod3结束线程:" + Thread.CurrentThread.ManagedThreadId.ToString());

        }

        private static string GetApi(string name)
        {
            Console.WriteLine("GetApi线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
            long result = 0;
            for (long i = 0; i < 1000000000; i++)
            {
                result += i;
            }
            return "我是Api返回的值:" + name;
        }

 

 可以看到,主线程里需要打印GetApi的返回值内容,所以主线程阻塞了,一直在等待,跟之前Func获取返回值是一样的情况,怎么解决,那就是放到回调函数里去。

var r = Task.Run<string>(() => GetApi("张三"));
            //Console.WriteLine(r.Result);

            //回调,将返回值放在回调里,不会阻塞主线程
            Task task = Task.Run(() =>
            {
                DoSomething("张三");
            });
            task.ContinueWith(t =>
            {
                Console.WriteLine("Task回调ContinueWith线程:" + Thread.CurrentThread.ManagedThreadId.ToString());
                Console.WriteLine(r.Result);
            });

 

posted @ 2021-01-03 21:15  luytest  阅读(107)  评论(0编辑  收藏  举报