关于异步,其实是个老生常谈的话题,也是各大公司面试常问的问题之一.本文就几个点来介绍异步解决的问题

注:对多线程的运行的基本机制要了解

1、介绍

有人可能会有疑问,为什么并行,非得用异步.多线程也已可以啊,多开两个线程不就行了.

案例分析:现在有一个生活场景.需要煮饭(假设需要20分钟-机器煮)、洗衣服(假设需要25分钟-机器洗)、蒸菜(假设需要10分钟-机器蒸).并且昨晚之后我需要告知哥们.

(1)、同步

如果采用同步的方式(我先煮饭,把米放到电饭煲里,然后站在电饭煲前面等,什么事都不能干,一直等到饭煮好.接下去在洗衣服....以此类推),那么我需要一件一件做,那么我总共要花费20+25+10=55分钟.显然这种方式很蠢.实例代码如下:

    class Program
{
static void Main(string[] args)
{
CookRice();
CookDish();
DoLaundry();
Console.WriteLine("哥们,全都搞定了");
Console.ReadKey();
} /// <summary>
/// 煮饭
/// </summary>
static void CookRice()
{
Thread.Sleep(20 * 1000 * 60);
} /// <summary>
/// 蒸菜
/// </summary>
static void CookDish()
{
Thread.Sleep(10 * 1000 * 60);
} /// <summary>
/// 洗衣服
/// </summary>
static void DoLaundry()
{
Thread.Sleep(25 * 1000 * 60);
}
}

(2)、多线程优化

ok,上面的代码执行方式显然是很蠢的,因为那样会占用我(线程)大量的时间.

注意:这里要从执行时间这个角度去考虑,因为作为Web开发,一个用户请求从开始到结束,是有时间限制的,如果你一个请求(报表之类的特殊业务除外,指互联网场景)超过1秒,用户可能都无法忍受。  这里的请求在.net下指代线程.

ok,那只能对这个耗时任务,进行拆解,当然前提是他可以拆解(存在并行化的可能),我们这个例子显然是可以的,于是,我这么做,先把米放到电饭煲里面,不在停留,接着立马把衣服放到洗衣机,最后再把菜放到蒸锅里.然后我去干别的事情了.代码如下:

    class Program
{
static void Main(string[] args)
{
var t1=Task.Run(() => CookRice());
var t2 = Task.Run(() => CookDish());
var t3 = Task.Run(() => DoLaundry());
Task.WaitAll(t1, t2, t3);
Console.WriteLine("哥们,全部搞定了");
Console.ReadKey();
} /// <summary>
/// 煮饭
/// </summary>
static void CookRice()
{
Thread.Sleep(20 * 1000 * 60);
} /// <summary>
/// 蒸菜
/// </summary>
static void CookDish()
{
Thread.Sleep(10 * 1000 * 60);
} /// <summary>
/// 洗衣服
/// </summary>
static void DoLaundry()
{
Thread.Sleep(25 * 1000 * 60);
}
}

ok,这里我们将大任务,拆分成了三个小任务,分别交给了三个线程去做.同时,我等待三个任务完成之后,告诉我哥们.这是整个任务的执行时间大大缩短,相当于原先一个人的活,交给了三个人干.能不快吗!

ok,到这里很多人觉得这样就行了.已经无法再继续优化了,这时候异步登场了.

(3)、异步优化

再优化代码之前,得知道线程池和CLR的概念,每个CLR会维护一个线程池.既然是池,说明线程的数量是有限的.并且我们的Web应用程序所使用的线程都会从CLR中去调取.那就说明,我们的Web程序能使用的线程有限.

ok.再回到上面的代码,

Task.WaitAll会阻塞主线程,主线程会在这里休眠,意味着这三个任务不做完,主线程会一直被占用.对应生活场景,就是我一直看着三台机器的执行,知道完成之后告诉我哥们.这期间我干不了任何事,只能看着.

那问题就大了.如果在高并发场景下.瞬时发起了1000条请求,那么就会产生非常多的等待线程,这些线程啥都不干,就干等着.造成了严重的资源浪费.显然是有问题的.

ok,异步登场了.

异步的原理(代码层面的介绍请百度),大致是这样,所有的线程不在等待,阻塞而是通过线程池调度,就是线程池主动通知.代码如下:

    class Program
{
static async void Main(string[] args)
{
Console.WriteLine($"当前是我在工作");
var t1=Task.Run(() => CookRice());
var t2 = Task.Run(() => CookDish());
var t3 = Task.Run(() => DoLaundry());
Console.WriteLine($"我触发了await操作,就返回上一个调用方法去干别的事情去了,同时通过状态机机制(自行百度),这个方法会被暂停");
await Task.WhenAll(t1, t2, t3);
Console.WriteLine("await 内部操作执行完毕,线程池委派了一个新线程来执行接下去的任务,状态机机制又会恢复当前方法,接下去执行");
Console.WriteLine("哥们,搞定了,但我不是你哥们,我是你哥们的朋友");
Console.ReadKey();
} /// <summary>
/// 煮饭
/// </summary>
static void CookRice()
{
Thread.Sleep(20 * 1000 * 60);
} /// <summary>
/// 蒸菜
/// </summary>
static void CookDish()
{
Thread.Sleep(10 * 1000 * 60);
} /// <summary>
/// 洗衣服
/// </summary>
static void DoLaundry()
{
Thread.Sleep(25 * 1000 * 60);
}
}

ok,上面的代码简要的阐述了异步的原理,通过async await编程模型,当Main方法执行到await之前时,我(主线程)就会回到上一个调用方法接着执行别的任务,如果没有返回线程池.接着通过状态机机制,暂停当前方法的执行,当await方法执行完毕时,线程池会委派新的线程回来接着执行接下去的方法,再次之前状态机会恢复方法的执行.以此类推.

通过这种方式(异步),我们的Web程序就能高效率的利用好线程.

(4)、异步在磁盘IO和网络请求上面的优势

同步程序在处理磁盘IO和网络请求时,同样会采用阻塞的方式,比如发起一个后端http、webscoket请求(使用同步方法)、文件读写请求等等,那么主线程等等到远程主机和硬件设备响应之后接着执行,期间他不会返回,会一直等.那么这和上面的问题是一样的了.这就是所谓处理IO-Bound Operation的方式,很显然,这也是一个异步操作。当我们希望进行一个异步的IO-Bound Operation时,CLR会(通过Windows API)发出一个IRP(I/O Request Packet)。当设备准备妥当,就会找出一个它“最想处理”的IRP(例如一个读取离当前磁头最近的数据的请求)并进行处理,处理完毕后设备将会(通过Windows)交还一个表示工作完成的IRP。CLR会为每个进程创建一个IOCP(I/O Completion Port)并和Windows操作系统一起维护。IOCP中一旦被放入表示完成的IRP之后(通过内部的ThreadPool.BindHandle完成),CLR就会尽快分配一个可用的线程用于继续接下去的任务。这种做法的需要一个重要条件,这就是发出用于请求的IRP的操作能够立即返回,并且这个IO操作不会使用任何线程。而此时,这种异步调用是真正地在节省资源,因为我们可以腾出线程用来处理其他任务了,但是这种做法据说需要操作系统和设备的支持,但是我实际测试发现使用异步Api的收益明显要高于同步.

2、总结

综上所述,异步的优势已经非常明显了,并且Web开发,基本都是要么和tcp要么和磁盘打交道.所以用异步个人认为是最佳实践.

.Net技术栈下的异步,你还在用同步方式进行开发吗?的更多相关文章

  1. 快速了解Scala技术栈

    http://www.infoq.com/cn/articles/scala-technology/ 我无可救药地成为了Scala的超级粉丝.在我使用Scala开发项目以及编写框架后,它就仿佛凝聚成为 ...

  2. 【Scala】Scala技术栈

    快速了解Scala技术栈 我无可救药地成为了Scala的超级粉丝.在我使用Scala开发项目以及编写框架后,它就仿佛凝聚成为一个巨大的黑洞,吸引力使我不得不飞向它,以至于开始背离Java.固然Java ...

  3. 前后端分离,我怎么就选择了 Spring Boot + Vue 技术栈?

    前两天又有小伙伴私信松哥,问题还是职业规划,Java 技术栈路线这种,实际上对于这一类问题我经常不太敢回答,每个人的情况都不太一样,而小伙伴也很少详细介绍自己的情况,大都是一两句话就把问题抛出来了,啥 ...

  4. 如何使用Microsoft技术栈

    Microsoft技术栈最近有大量的变迁,这使得开发人员和领导者都想知道他们到底应该关注哪些技术.Microsoft自己并不想从官方层面上反对Silverlight这样的技术,相对而言他们更喜欢让这种 ...

  5. “MEAN”技术栈开发web应用

    “MEAN”技术栈开发web应用 上一篇我们讲了如何使用angular搭建起项目的前端框架,前端抽象出一个service层来向后端发送请求,后端则返回相应的json数据.本篇我们来介绍一下,如何在no ...

  6. Vuejs技术栈从CLI到打包上线实战全解析

    前言 本文是自己vue项目实践中的一些总结,针对Vue2及相关技术栈,实践中版本为2.3.3. 开发前须知 vue-cli 在开发前,我们要至少通读一遍vue官方文档和API(看官方文档是最重要的,胜 ...

  7. Elastic 技术栈之 Logstash 基础

    title: Elastic 技术栈之 Logstash 基础 date: 2017-12-26 categories: javatool tags: java javatool log elasti ...

  8. 重谈react优势——react技术栈回顾

    react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ...

  9. java十年技术栈[总结复习用]

    以下摘自http://www.tvtv223.com/so/8/default/8.html#36-数据库的分库分表mycat java技术栈 参考了众多资料,这里就不再详细列举了,可以自行去搜索 1 ...

随机推荐

  1. 在vscode中配置sass savepath

    1.先在VSCode上面安装插件:Live Sass Compiler 2.创建好scss文件夹文件和css文件夹 3.然后在VSCode的控制台上打开Live sass watching模式(控制台 ...

  2. Java并发包源码学习系列:AQS共享式与独占式获取与释放资源的区别

    目录 Java并发包源码学习系列:AQS共享模式获取与释放资源 独占式获取资源 void acquire(int arg) boolean acquireQueued(Node, int) 独占式释放 ...

  3. 消息队列 ---常用的 MQ 中间件

    目前市面上比较常用的 MQ(Message Queue,消息队列)中间件有 RabbitMQ.Kafka.RocketMQ,如果是轻量级的消息队列可以使用 Redis 提供的消息队列,其中 Redis ...

  4. ABP vNext 自动注入,暗藏天坑如斯

    导言 我们在使用ABP vNext框架时,都知道该框架为我们实现了自动依赖注入(实现自动注入需要在项目里面创建Module类,并且将Module类上的DependsOn到相应的启动Module类或调用 ...

  5. #3使用html+css+js制作网页 番外篇 制作接收php

    使用html+css+js制作网页 番外篇 制作接收php 本系列链接 基础 php语法 例子 本系列链接 #1使用html+css+js制作网站教程 准备 #2使用html+css+js制作网站教程 ...

  6. NOIP初赛篇——10计算机网络

    网络的定义 ​ 所谓计算机网络,就是利用通信线路和设备,把分布在不同地理位置上的多台计算机连接起来. ​ 计算机网络是现代通信技术与计算机奇数结合的产物. ​ 网络中计算机与计算机之间的通信依靠协议进 ...

  7. 【JavaWeb】jQuery 基础

    jQuery 基础 介绍 顾名思义,它是 JavaScript 和 查询,是辅助 JavaScript 开发的类库. 它的核心思想是 write less, do more. 所以它实现了很多浏览器的 ...

  8. 使用OpenCV进行简单的人像分割与合成

    图像合成 实现思路 通过背景建模的方法,对源图像中的动态人物前景进行分割,再将目标图像作为背景,进行合成操作,获得一个可用的合成影像. 实现步骤如下. 使用BackgroundSubtractorMO ...

  9. mysql过滤复制

  10. 【Oracle】密码文件相关

    Oracle数据库的orapwd命令,主要用来建立密码(口令)文件. 一.查看帮助信息 [oracle@oracle11g dbs]$ orapwd Usage: orapwd file=<fn ...