Async Await异步调用WebApi

在使用它们之前 我们先关心下 为什么要使用它们。好比 一个人做几件事,那他得一件一件的做完,而如果添加几个人手一起帮着做很显然任务会更快的做好。这就是并行的粗浅含义。

在程序中,常见的性能瓶颈在于 NetWork I/O 瓶颈 , CPU 瓶颈, 数据库I/O瓶颈,这些瓶颈使得我们的程序运行的很慢,我们想办法去优化。因为并行开发本身就加重CPU负担,所以一般将并行用来优化 由另外两种I/O造成的瓶颈。


如果想使用异步api接口,一般的动机是在方法里面可能使用Task.Run 进行异步的去处理一个耗时的操作。对于存在耗时的api接口,使用异步api接口(接口内部可能使用 Task.Run 异步操作)不一定可以提高响应。

举个例子:

public async Task<IEnumerable<string>> Get(string s)

{

    await Task.Run(()=>Job1(s));

    return new string[] { "value1", "value2" };

}

        

private void Job1(string s)

{

    return;

}

情况1.假定Job1非常耗时,而且Job1的结果对下文的结果输出并没有什么影响,这个时候如果使用 await Task.Run(()=>Job1(s)); 就显得非常不明智。可以去掉await关键字,把Job1放在一个异步的线程中去执行,而程序继续往下走,避免不必要的等待。


情况2.更为常见的:Job1的结果是下文所需要的。这种情况下,使用await Task.Run(()=>Job1(s));的意义就不大了。同步和异步有着同样的效率。

C#中 Task.Delay()和Thread.Sleep() 的区别:
Thread.Sleep()是同步延迟,Task.Delay()是异步延迟。
Thread.Sleep()会阻塞线程,Task.Delay()不会。
Thread.Sleep()不能取消,Task.Delay()可以。
Task.Delay()实质创建一个运行给定时间的任务,Thread.Sleep()使当前线程休眠给定时间。
反编译Task.Delay(),基本上讲它就是个包裹在任务中的定时器。
Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。

    边的两个方法, 通过Thread.Sleep来模拟程序耗时5秒。

        //sync method sample

        public static void DownLoadWebPage()

        {

            //TODO  cost 5s           

            Console.WriteLine( "DownLoadWebPage on Thread:{0}", Thread.CurrentThread.ManagedThreadId);

            Thread.Sleep(5000);

            Console.WriteLine( "End downloading the page.." );

        }


        public static void LoadDatafromDB()

        {

            //TODO  cost 5s           

            Console.WriteLine( "LoadDataFromDB on Thread:{0}", Thread.CurrentThread.ManagedThreadId);

            Thread.Sleep(5000);

            Console.WriteLine( "End loading Data.." );

        }

    那如果我们再写一个方法来调用

        public static void OurSyncJob()

        {

            Console.WriteLine( "start doing things sync" );

            DownLoadWebPage();

            LoadDatafromDB();

            //do some other things

            Console.WriteLine( "do some other things" );           

        }

    很显然 会耗时超过10s钟,如果我们用task开启两个线程 同时执行 如下

        public static async Task OurAsyncJobTask()

        {

            Console.WriteLine( "start doing things async" );

            var taskA= Task.Run(() => { DownLoadWebPage(); });

            var taskB= Task.Run(() => { LoadDatafromDB(); });

            await Task.WhenAll(taskA,taskB);                  

            Console.WriteLine( "do some other things" );

        }


    那执行时间只会是5s多一点, 大大提升了我们程序的性能。

 

    在了解了这些基础之后,我们来接触异步程序的实际运用场景。

    我们调用 WebApi的时候,因为要经过网络传输,有时候会很慢。 这时候便有了我们用异步一展身手的时候了。

    我们的webapi如下:

    public class ProductController : ApiController

    {

        public productRepo repo = new productRepo();

        public IEnumerable< Product> getProducts()

        {

            Thread.Sleep(5000); 

            return repo.GetAll();

        }

    }


    public class WidgetController : ApiController

    {

        public widgetRepo repo = new widgetRepo();

        public IEnumerable< Widget> getWidgets()

        {

            Thread.Sleep(5000);

            return repo.GetAll();

        }

    }

    都是模拟耗时5秒钟,现在要同时调用这些api获得 数据并一起展示

    public static List <Product > TaskGetProduct()

    {

       using( HttpClient client= new HttpClient())

       {

                client.BaseAddress = new Uri( "http://localhost:52593/" );

                client.DefaultRequestHeaders.Accept.Clear();

                client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue ("application/json" ));

                string json = client.GetString("api/Product/Products" );

                return JsonConvert.DeserializeObject< List< Product>>(json);

       }

    }


    很显然 调用这个webapi要5秒多,那么我们要同时获取的时候,就要分别调用TaskGetProduct() TaskGetWidget() TaskGetGizmos()

    和前面的经验一样,这要是同步的话不得15秒多。。这要如何忍受。。

    很显然要异步获取

        public static async Task< List< Product>> TaskGetProduct()

        {

            using( HttpClient client= new HttpClient())

            {

                client.BaseAddress = new Uri( "http://localhost:52593/" );

                client.DefaultRequestHeaders.Accept.Clear();

                client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue ("application/json" ));

                string json = await client.GetStringAsync("api/Product/Products" );

                return JsonConvert.DeserializeObject< List< Product>>(json);

            }

        }

       public static async Task< pwgVM> RunTaskGetAll()

        {

            var task1 = TaskGetItem< Product>();

            var task2 = TaskGetItem< Gizmos>();

            var task3 = TaskGetItem< Widget>();

            await Task.WhenAll(task1,task2,task3);

            pwgVM vm = new pwgVM(task1.Result,task2.Result,task3.Result);

            return vm;

        }


三个任务同时进行,花费5秒多。ok