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

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

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. 杭电1720---A+B Coming(技巧:使用%x)

    Problem Description Many classmates said to me that A+B is must needs. If you can't AC this problem, ...

  2. Xamarin.Forms 5.0 来了

    Xamarin.Forms 5.0 已经正式发布,并带来其新功能,具体看官方博客https://devblogs.microsoft.com/xamarin/xamarin-forms-5-0-is- ...

  3. CentOS 6安装Gitlab

    1. 保证CentOS 6能连接网络 . 2.安装依赖: sudo yum install -y curl policycoreutils-python openssh-server cronie s ...

  4. JavaScript 获取当天0点以及当前时间方法

    js 取得今天0点: const start = new Date(new Date(new Date().toLocaleDateString()).getTime()); console.log( ...

  5. 【Flutter】容器类组件之装饰容器

    前言 DecoratedBox可以在其子组件绘制前后绘制一些装饰,例如背景,边框,渐变等. 接口描述 const DecoratedBox({ Key key, // 代表要绘制的装饰 @requir ...

  6. 简单的JS+CSS实现网页自定义换肤

     1,实现效果 2,实现原理 主要原理是利用css变量设置颜色,用js动态修改变量,使颜色变化,兼容性如下: 实现换肤之前先要了解一下伪类选择器 :root ,还有css的 var() 函数和 set ...

  7. MySQL select join on 连表查询和自连接查询

    连表查询 JOIN ON 操作 描述 inner join 只返回匹配的值 right join 会从右表中返回所有的值, 即使左表中没有匹配 left join 会从左表中返回所有的值, 即使右表中 ...

  8. 使用K8s的一些经验和体会

    坑 Java应用程序的奇怪案例 ​ 在微服务和容器化方面,工程师倾向于避免使用 Java,这主要是由于 Java 臭名昭著的内存管理.但是,现在情况发生了改变,过去几年来 Java 的容器兼容性得到了 ...

  9. 【Linux】ssh远程连接到指定ip的指定用户上

    通过ssh可以远程连接到其他的机器上,但是如果只想连接到指定的用户的话 需要这样做: -l 选项 (是L不是I,小写) ssh IP -l 用户名 这里的ip如果在hosts下就可以直接输入域名或者主 ...

  10. ctfshow—web—web签到题

    打开靶机,发现只有一句话 查看源码 发现一段字符串,猜是base64加密 拿到flag