• 转[C#] 谈谈异步编程async await


    [C#] 谈谈异步编程async await

     

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

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

      期间会有

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

      推荐先看后顶,学的更快!

    Hello World

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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 线程,这两次获取的线程可能相同,也可能会不同。如果有比较耗时的任务,非常建议把同步请求转换为异步。

    线程安全和信号量

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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 { getset; }

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

      

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

    线程锁例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    private static readonly object lockObj = new object();
            public static bool IsComplete { getset; }
            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)例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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();
            }

    上面执行结果

    从 .NET Framework 4.5 和 Windows 运行时中列出的 API 包含支持异步编程的方法。

    应用程序区域

    包含异步方法的受支持的 API

    Web 访问

    HttpClientSyndicationClient

    使用文件

    StorageFileStreamWriterStreamReaderXmlReader

    使用图像

    MediaCaptureBitmapEncoderBitmapDecoder

    WCF 编程

    同步和异步操作

    作者:Never、C

    本文链接:http://www.cnblogs.com/neverc/p/4368821.html

  • 相关阅读:
    安卓系统的文件管理神器Solid Explorer(v2.2)
    地月距离竟然如此遥远
    Android在争议中逃离Linux内核的GPL约束【转】
    gearman
    PHP基础学习
    函数式编程
    有向图的实现
    无向图的实现
    百度地图API获取数据
    python队列的实现
  • 原文地址:https://www.cnblogs.com/dingjin/p/8492906.html
Copyright © 2020-2023  润新知