.NET Task,async,await的详解

Task

  同步和异步

  说Task之前,先说一个基本概念,异步,正常的程序在执行时会按照调用的先后顺序执行,当一个靠前的方法还没有执行完毕,就不会执行后面的代码,而异步就是让一个方法的执行过程独立出去,当执行一个异步方法或一段异步代码时,这个方法或代码不会占用主线程,而是新创建一个线程与主线程同时执行。这样就可以让那些很耗资源的加载代码异步执行,让主线程空出来做别的事,比如显示加载进度、绘制界面之类的。
  Task

  在Task之前多线程是用ThreadPool实现的,虽然确实能实现多线程,但不能控制线程的执行顺序,也不能获取线程内程序的执行状况(异常或成功),所以.NET4.0在ThreadPool的基础上推出了Task,保留了ThreadPool优点的同时,也解决了ThreadPool的问题。Task线程的创建主要有三种方式:

            //1.new方式实例化一个Task,需要通过Start方法启动
            Task task = new Task(() =>
            {
                Thread.Sleep(100);
                Console.WriteLine($"hello, task1的线程ID为{Thread.CurrentThread.ManagedThreadId}");
            });
            task.Start();

            //2.Task.Factory.StartNew(Action action)创建和启动一个Task
            Task task2 = Task.Factory.StartNew(() =>
              {
                  Thread.Sleep(100);
                  Console.WriteLine($"hello, task2的线程ID为{ Thread.CurrentThread.ManagedThreadId}");
              });

            //3.Task.Run(Action action)将任务放在线程池队列,返回并启动一个Task
            Task task3 = Task.Run(() =>
              {
                  Thread.Sleep(100);
                  Console.WriteLine($"hello, task3的线程ID为{ Thread.CurrentThread.ManagedThreadId}");
              });
            Console.WriteLine("执行主线程!");        

   执行结果:

  执行主线程!
  hello, task2的线程ID为6
  hello, task1的线程ID为5
  hello, task3的线程ID为4

  上面展示的是没有返回值的Task,可以看到Task并没有影响到主进程的执行,程序先执行完了主线程,再执行异步的线程。我们也可以创建有返回值的Task:

 

static void Main(string[] args)
        {
            Task<string> tt1 = t1();
            tt1.Start();
            Task<string> tt2 = t1();
            tt2.Start();
            Task<string> tt3 = t1();
            Task<string> tt4 = t1();
            tt4.Start();
            Console.WriteLine("tt1:"+tt1.Result);
            Console.WriteLine("执行主线程!");           
            Console.WriteLine("tt2:"+tt2.Result);
            Console.WriteLine("tt3:"+tt3.Result);
            Console.WriteLine("tt4:"+tt4.Result);
            Console.ReadKey();
            
        }
        public static Task<string> t1()
        {
            Thread.Sleep(1000);
            return new Task<string>(()=> { return "线程ID:" + Thread.CurrentThread.ManagedThreadId; });
            
        }

 

 

 

 

执行结果:

tt1:线程ID:4
执行主线程!
tt2:线程ID:5

  Task.Result可以获得Task委托的执行结果,但会阻塞主线程,因为tt3没有start所以不会返回值,但主线程一直在等待tt3的执行结果,导致了tt4的结果一致没能输出,但实际上tt4已经执行结束了。

Async/await

  Async是方法的修饰符,修饰的方法返回值必须为Task、Task<>、void。必须和await一起使用才有意义,而await用在Task委托的前面,效果先看代码:

static void Main(string[] args)
        {
            Task<string> tt1 = t1();
            tt1.Start();
            tt1.Wait();
            Console.WriteLine("执行主线程");
            Console.WriteLine(t2().Result);
            Console.ReadKey();
            
        }
        public static Task<string> t1()
        {
            Thread.Sleep(1000);
            Console.WriteLine("t1:线程ID:" + Thread.CurrentThread.ManagedThreadId);
            return new Task<string>(() => { return "t1:线程ID:" + Thread.CurrentThread.ManagedThreadId; });

        }
        public static async Task<string> t2()
        {
            Task<string> tt2 = Getstr();
            await tt2;
            string str = tt2.Result;
            return str;
        }
        public static Task<string> Getstr()
        {
            Thread.Sleep(1000);
            return Task<string>.Run(() => { return "Getstr"; });
        }

输出结果:

t1:线程ID:1
执行主线程
Getstr

  Task.wait的效果是让主线程阻塞,等待Task执行完成再继续执行,而await效果相同,不过阻塞的是t2方法,也就是await修饰的方法,到这里可能会有人想,如果让主线程等待其他线程,那不就和同步一样了吗,看上去确实差不多,但await并不会造成线程的阻塞(web程序感觉不到)。

  最后需要说的是,线程并不能提升程序的执行速度,只能提升程序的执行效率(吞吐量)。同步异步各有优劣,需要分情况选择。

 

posted @ 2022-10-10 09:24  lrplrplrp  阅读(304)  评论(0编辑  收藏  举报