zoukankan      html  css  js  c++  java
  • 异步编程

    前段时间, 拿到一个框架, 之前也没怎么看, 只记得里面使用了蛮多的异步. 

    public async Task<ActionResult> Login(LoginModel model, string returnUrl)

    之前的项目中, 没有使用过异步的. 可能有人会把多线程和异步混为一谈, 其实还是不一样的东西.

    那么, 今天就先来学习下异步, 以备使用之需. 这里只介绍新的方式了, 至于之前老的方式, 有些复杂, 没有新方式直观, 简洁.

    一. 知识点

    异步方法:提供了一种简便方式完成可能需要长时间运行的工作,而不必阻止调用方的线程。 异步方法的调用方可以继续工作,而不必等待异步方法完成。

    await:运算符应用于一个异步方法的任务挂起方法的执行,直到等待任务完成。 任务表示正在进行的工作。 await 表达式不阻止它在其上执行的线程

    async: async 修饰符指示方法、它进行修改 lambda 表达式或 匿名方法 是异步的

    Task类:它表示一个任务,在.net4.5版本开始被支持, 它隶属于 System.Threading.Tasks命名空间下;通过Task类可以方便的开启一个新的线程。

    二、小Demo

    1.mvc文件下载

     在mvc源码解析的时候, 不知道我有没有提过, mvc在默认模式下, 是使用的异步方式来完成工作的. 那先来看一个mvc里面的应用吧

    public class HomeController : Controller
    {
        public async Task<FileResult> DownLoad(string name)
        {
            var path = Server.MapPath("~/FileRes/") + name;
            using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.Asynchronous))
            {
                byte[] data = new byte[fs.Length];
                await fs.ReadAsync(data, 0, data.Length);
                return File(data, "application/octet-stream", name);
            }
        }
    }

    这里读取文件, 是以异步的方式来读取的, 当读取完成之后, 就会将fs关掉, 所以在返回文件的时候, 并不能使用 return File(fs,"application/octet-stream",name)的方式去返回了. 

    2. 同步方法调用异步方法, 以及异步方法调用异步方法

    static void Main(string[] args)
    {
        Console.WriteLine("Main Start : " + Thread.CurrentThread.ManagedThreadId);
        Step1();
        Step2();
        Console.WriteLine("Main End : " + Thread.CurrentThread.ManagedThreadId);
        Console.ReadKey();
    }
    
    static async void Step1()
    {
        Console.WriteLine("Step1 start : " + Thread.CurrentThread.ManagedThreadId);
        try
        {
            await Task.Run(() => {
                Console.WriteLine("Step1.1 Current sleeping ThreadID : " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(3000);
            });
    
            await Task.Run(() =>
            {
                Console.WriteLine("Step1.2 Current sleeping ThreadID : " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(3000);
                Console.WriteLine("ThreadTest.Test Runing : " + Thread.CurrentThread.ManagedThreadId);
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine("ThreadTest : " + ex.Message);
        }
        await Step3();
        Console.WriteLine("Step1 end : " +Thread.CurrentThread.ManagedThreadId);
    }
    
    static void Step2()
    {
        Console.WriteLine("Step2 start : " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("Step2 Current ThreadID : " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("Step2 end : " + Thread.CurrentThread.ManagedThreadId);
    }
    
    static async Task Step3()
    {
        Console.WriteLine("Step3 start : " + Thread.CurrentThread.ManagedThreadId);
        await Task.Run(() =>
        {
            Console.WriteLine("Step3 Current sleeping ThreadID : " + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(5000);
        });
        Console.WriteLine("Step3 end : " + Thread.CurrentThread.ManagedThreadId);
    }

    Main方法调用方法Step2是同步方法调用同步方法, 调用Step1是同步方法调用异步方法.

    Step1方法调用Step3方法, 是异步方法调用异步方法.

    运行结果:

    这里我运行了好几次, 从上面的结果, 可以看出以下几点:

    1. 主线程全程没有被阻塞, 一直执行到结束.

      如果我在Step2方法中, 加上一句 : Thread.Sleep(10), 那么主线程就会被阻塞在这里, 等待10s中的时间. 啥事都不用干的感觉, 真好.

    2. 主线程在Step1方法中, 碰到await等待的时候, 完全没理会, 直接回到Main方法中, 去执行Step2方法了.

      这里就是同步方法调用异步方法时要注意的地方, 因为同步方法不会理await的, 并不会乖乖的在这里等await后面的方法执行结束, 还有一点, await下面的方法, 主程序也不会去管了.

    3. 注意到这里的Step1.1和Step1.2的执行线程, 有时候相同, 有时候不同. 这是为什么呢?

      这些线程应该都是从线程池中取的, 在异步方法中, 如果当前正在使用的线程已经不需要用了, 会把他还给线程池, 由线程池再分配出去, 给别的线程用. 当休息时间到了之后, 会再向线程池请求线程来完成下面的工作. 有点像打车, 到了一个地方之后, 下车了, 就把出租车这个资源释放掉了, 等一个小时之后, 要回去了, 再打一个车, 这时候的出租车, 可能是之前打的那辆, 当然也可能不是. 这样做的一个好处, 就是能增加吞吐量, 提升并发处理能力. 这里出租车如果一直在这里等你的话, 你是不需要付钱的, 赚的少了, 出租车可不怎么愿意了, 哈哈. 

     4. Step1调用Step3的时候, 很明显的异步方法调用异步方法, 那么他会不会像之前的同步方法调用异步方法那样呢?

      从上面的图中, 就能很清晰的看到, 并不是一样的. 效果上, 像同步方法调用同步方法那样, 等在这里, 一直等你到天荒地老. 

    猜想:

    如果从计算机原理上来分析这里的异步挂起切换线程, 我猜想过程应该是这样子的:

      当程序运行到sleep之前的时候, 会将数据什么的存入到存储器和寄存器, 碰到sleep的时候, 就会释放线程, 开启定时器, 定时器到点触发, 通知线程池, 我这边需要一条线程来继续处理, 此时线程池收到消息, 会去池中查看哪些线程可用, 如果都没有, 会先等待一会, 可能是0.5s吧, 如果还没有可用的线程释放出来, 就会创建一条线程, 来满足要求, 完成工作.

    参考:

     详解.NET异步

  • 相关阅读:
    Selenium入门15 截图
    selenium入门14 窗口切换
    Locust的官网及安装
    命令行输入Jmeter提示不是内部或外部命令,处理方式:添加环境变量
    python pip安装报错python setup.py egg_info failed with error code 1
    Selenium入门13 cookie的增删改查
    Selenium入门12 鼠标和键盘事件
    Selenium入门11 滚动条控制(通过js)
    Selenium入门10 弹出框的处理 switch_to.alert
    Selenium入门9 上传文件
  • 原文地址:https://www.cnblogs.com/elvinle/p/6474018.html
Copyright © 2011-2022 走看看