zoukankan      html  css  js  c++  java
  • 【.NET】- async await 异步编程

            为什么需要异步,异步对可能起阻止作用的活动(例如,应用程序访问 Web 时)至关重要。 对 Web 资源的访问有时很慢或会延迟。 如果此类活动在同步过程中受阻,则整个应用程序必须等待。 在异步过程中,应用程序可继续执行不依赖 Web 资源的其他工作,直至潜在阻止任务完成。

      本节将一步一步带领大家理解async和await。

      期间会有

      Hello World原理介绍异步会提高程序的运行速度吗async和awaitMVC中的异步Action,以及线程中常涉及到的线程安全信号量

    Hello World

    static void Main(string[] args)
    {
        new Thread(Test) { IsBackground = false }.Start();      //.Net 在1.0的时候,就已经提供最基本的API.
        ThreadPool.QueueUserWorkItem(o => Test());              //线程池中取空闲线程执行委托(方法)
        Task.Run((Action)Test);                                 //.Net 4.0以上可用
        Console.WriteLine("Main Thread");
        Console.ReadLine();
    }
     
    static void Test()
    {
        Thread.Sleep(1000);
        Console.WriteLine("Hello World");

    原理

      其实不管是Task,ThreadPool,本质最终都是Thread。只不过微软帮我们在简化线程控制的复杂度。

      线程池是CLR中事先定义好的一些线程。Task取的线程池,只不过在语法上,可以非常方便取返回值。

    异步会提高程序的运行速度吗

      多线程会提高程序的效率,不会提高运行速度。

      这就好比这一个任务让前台花1个小时。前台完成10分钟的时候

      打电话给经理,让他安排一个人来干30分钟(new Thread()),他干剩下的20分钟。(创建线程,需要时间,内存资源)

      或者从旁边空闲的同事中(ThreadPool 或 Task),拉一个人过来干30分钟。他干剩下的20分钟。(需要的时间少,资源本来就存在)

      从上看出,异步会让一份任务时间变长。资源消耗更多。但是可以让前台(UI线程)空闲下来,听从领导(用户)指挥。

    async和await只是一个标记

      首先看个Demo:

    static void Main(string[] args)
    {
        Task.Run(() =>                                          //异步开始执行
        {
            Thread.Sleep(1000);                                 //异步执行一些任务
            Console.WriteLine("Hello World");                   //异步执行完成标记
        });
        Thread.Sleep(1100);                                     //主线程在执行一些任务
        Console.WriteLine("Main Thread");                       //主线程完成标记
        Console.ReadLine();
    }

    执行结果:

      这个很正常。但是我们希望先执行主线程完成标记,不改动主线程和Task的任务情况下,如何处理?

    使用await和async:

    static void Main(string[] args)
    {
        Say();                             //由于Main不能使用async标记
        Console.ReadLine();
    }
    private async static void Say() { var t = TestAsync(); Thread.Sleep(1100); //主线程在执行一些任务 Console.WriteLine("Main Thread"); //主线程完成标记 Console.WriteLine(await t); //await 主线程等待取异步返回结果 }
    static async Task<string> TestAsync() { return await Task.Run(() => { Thread.Sleep(1000); //异步执行一些任务 return "Hello World"; //异步执行完成标记 }); }

      1.凡是使用await关键字的方法,都必须打上async标记。

      2.async表示方法内有异步方法,调用async方法,会立刻另起线程执行。

      3.await只是显示等待线程结束。await表示等待异步方法执行完,并取返回值。

    MVC中的异步Action

      既然多线程不能提高运行速度,而且每次请求Asp.net程序都是发起一个新的线程,为什么还要用多线程让其“降速”?

      为了提高网站的吞吐量。

      在MVC中,如果采用异步Action,则会像下面情况执行。

      1.请求到达IIS,IIS应用程序池分配一个worker线程用来响应请求。

      2.worker线程,执行异步操作,调用CLR线程池线程处理。

      3.释放worker线程,响应其他请求。

      4.异步操作执行完,w3wp(应用程序池进程)再次分配一个worker线程继续响应。

      上述使用场景中,会获取两次worker 线程,这两次获取的线程可能相同,也可能会不同。如果有比较耗时的任务,非常建议把同步请求转换为异步。

    线程安全和信号量

       先举个线程不安全的例子。

    static void Main(string[] args)
    {
        Task.Run((Action)Test);
        Task.Run((Action)Test);
        Console.ReadLine();
    }
     
    private static void Test()
    {
        if (!IsComplete)
        {
            //todo other
            Thread.Sleep(500);
            Console.WriteLine("执行完成");
            IsComplete = true;
        }
    }
     
    public static bool IsComplete { get; set; }

      上面的执行结果,这就是线程不安全。(多线程访问同一段代码 产生不确定结果。)

      

    如何解决,涉及到线程锁的概念。线程锁会让多线程访问的时候,一次只允许一个线程进入。

    线程锁例子

    private static readonly object lockObj = new object();
            public static bool IsComplete { get; set; }
            static void Main(string[] args)
            {
                Task.Run((Action)Test);
                Task.Run((Action)Test);
                Console.ReadLine();
            }
     
            private static void Test()
            {
                lock (lockObj)                              //锁住的必须是引用类型。由于在静态方法中,则锁住静态引用类型。
                {
                    if (!IsComplete)
                    {
                        //todo other
                        Thread.Sleep(500);
                        Console.WriteLine("执行完成");
                        IsComplete = true;
                    }
                }
            }

      

    信号量

      线程锁的技术使一块代码只能一个线程进入。信号量的存在,则是让同一块代码指定多个线程进入。

    信号量(SemaphoreSlim)例子

    static readonly SemaphoreSlim slim = new SemaphoreSlim(2);
            static void Main(string[] args)
            {
                for (int i = 0; i < 5; i++)
                {
                    ThreadPool.QueueUserWorkItem(Test, i);
                }
                Console.ReadLine();
            }
     
            private async static void Test(object i)
            {
                Console.WriteLine("准备执行" + i);
                await slim.WaitAsync();
                Console.WriteLine("开始执行" + i);
                //todo other
                await Task.Delay(1000);
                Console.WriteLine("执行结束" + i);
                slim.Release();
            }

    上面执行结果

  • 相关阅读:
    随机森林算法参数调优
    BAYES和朴素BAYES
    阿里云 金融接口 token PHP
    PHP mysql 按时间分组 表格table 跨度 rowspan
    MySql按周,按月,按日分组统计数据
    PHP 获取今日、昨日、本周、上周、本月的等等常用的起始时间戳和结束时间戳的时间处理类
    thinkphp5 tp5 会话控制 session 登录 退出 检查检验登录 判断是否应该跳转到上次url
    微信 模板消息
    php 腾讯 地图 api 计算 坐标 两点 距离 微信 网页 WebService API
    php添加http头禁止浏览器缓存
  • 原文地址:https://www.cnblogs.com/wangwust/p/9474786.html
Copyright © 2011-2022 走看看