.Net技术栈下的异步,你还在用同步方式进行开发吗?
关于异步,其实是个老生常谈的话题,也是各大公司面试常问的问题之一.本文就几个点来介绍异步解决的问题
注:对多线程的运行的基本机制要了解
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技术栈下的异步,你还在用同步方式进行开发吗?的更多相关文章
- 快速了解Scala技术栈
http://www.infoq.com/cn/articles/scala-technology/ 我无可救药地成为了Scala的超级粉丝.在我使用Scala开发项目以及编写框架后,它就仿佛凝聚成为 ...
- 【Scala】Scala技术栈
快速了解Scala技术栈 我无可救药地成为了Scala的超级粉丝.在我使用Scala开发项目以及编写框架后,它就仿佛凝聚成为一个巨大的黑洞,吸引力使我不得不飞向它,以至于开始背离Java.固然Java ...
- 前后端分离,我怎么就选择了 Spring Boot + Vue 技术栈?
前两天又有小伙伴私信松哥,问题还是职业规划,Java 技术栈路线这种,实际上对于这一类问题我经常不太敢回答,每个人的情况都不太一样,而小伙伴也很少详细介绍自己的情况,大都是一两句话就把问题抛出来了,啥 ...
- 如何使用Microsoft技术栈
Microsoft技术栈最近有大量的变迁,这使得开发人员和领导者都想知道他们到底应该关注哪些技术.Microsoft自己并不想从官方层面上反对Silverlight这样的技术,相对而言他们更喜欢让这种 ...
- “MEAN”技术栈开发web应用
“MEAN”技术栈开发web应用 上一篇我们讲了如何使用angular搭建起项目的前端框架,前端抽象出一个service层来向后端发送请求,后端则返回相应的json数据.本篇我们来介绍一下,如何在no ...
- Vuejs技术栈从CLI到打包上线实战全解析
前言 本文是自己vue项目实践中的一些总结,针对Vue2及相关技术栈,实践中版本为2.3.3. 开发前须知 vue-cli 在开发前,我们要至少通读一遍vue官方文档和API(看官方文档是最重要的,胜 ...
- Elastic 技术栈之 Logstash 基础
title: Elastic 技术栈之 Logstash 基础 date: 2017-12-26 categories: javatool tags: java javatool log elasti ...
- 重谈react优势——react技术栈回顾
react刚刚推出的时候,讲react优势搜索结果是几十页. 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少坑,加过多少班,血泪控诉也不下千文. 今天,再谈一遍react优 ...
- java十年技术栈[总结复习用]
以下摘自http://www.tvtv223.com/so/8/default/8.html#36-数据库的分库分表mycat java技术栈 参考了众多资料,这里就不再详细列举了,可以自行去搜索 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, ...
- Xamarin.Forms 5.0 来了
Xamarin.Forms 5.0 已经正式发布,并带来其新功能,具体看官方博客https://devblogs.microsoft.com/xamarin/xamarin-forms-5-0-is- ...
- CentOS 6安装Gitlab
1. 保证CentOS 6能连接网络 . 2.安装依赖: sudo yum install -y curl policycoreutils-python openssh-server cronie s ...
- JavaScript 获取当天0点以及当前时间方法
js 取得今天0点: const start = new Date(new Date(new Date().toLocaleDateString()).getTime()); console.log( ...
- 【Flutter】容器类组件之装饰容器
前言 DecoratedBox可以在其子组件绘制前后绘制一些装饰,例如背景,边框,渐变等. 接口描述 const DecoratedBox({ Key key, // 代表要绘制的装饰 @requir ...
- 简单的JS+CSS实现网页自定义换肤
1,实现效果 2,实现原理 主要原理是利用css变量设置颜色,用js动态修改变量,使颜色变化,兼容性如下: 实现换肤之前先要了解一下伪类选择器 :root ,还有css的 var() 函数和 set ...
- MySQL select join on 连表查询和自连接查询
连表查询 JOIN ON 操作 描述 inner join 只返回匹配的值 right join 会从右表中返回所有的值, 即使左表中没有匹配 left join 会从左表中返回所有的值, 即使右表中 ...
- 使用K8s的一些经验和体会
坑 Java应用程序的奇怪案例 在微服务和容器化方面,工程师倾向于避免使用 Java,这主要是由于 Java 臭名昭著的内存管理.但是,现在情况发生了改变,过去几年来 Java 的容器兼容性得到了 ...
- 【Linux】ssh远程连接到指定ip的指定用户上
通过ssh可以远程连接到其他的机器上,但是如果只想连接到指定的用户的话 需要这样做: -l 选项 (是L不是I,小写) ssh IP -l 用户名 这里的ip如果在hosts下就可以直接输入域名或者主 ...
- ctfshow—web—web签到题
打开靶机,发现只有一句话 查看源码 发现一段字符串,猜是base64加密 拿到flag