Task及Mvc的异步控制器 使用探索
微软的Task已经出来很久了,一直没有去研究,以为就是和Thread差不多的东西。直到最近看到了Task的使用介绍,发现比Thread的语法要精炼多了,于是便在项目中用上了。
结果就出问题了,数据库连接池用一段时间就满了,排除了各种原因,最后开始怀疑是不是Task有什么不为人知的隐患。
由于对Task的使用只是停留在开一个线程去执行一个不需要返回结果的任务这种阶段。为了查明是否是Task引起的线程池满,便开始各种查资料。
最终的结果是,连接池满是因为程序中的一个SqlConnection没有关闭,和Task没有半毛钱关系......
问题解决了。Task也研究的差不多了。
下面我们来谈一下Task的使用.....
开启一个Task
开启task有以下三种方式,曾经一度纠结在到底该用哪种方式来开始一个任务,最终发现其实在没有特殊要求的情况下,这三种方式除了语法不同外,执行方式和结果是一样的
Task<int> t1 = Task.Factory.StartNew(() => { Task.Delay(); return ; });
Task<int> t2 = new Task<int>(() => { Task.Delay(); return ; });
t2.Start();
Task<int> t3 = Task.Run(() => { Task.Delay(); return ; });
上面的示例返回一个数字,所以接收者是Task的泛型,同样也可以执行一个不带返回结果的函数。
Task返回值
1.可以直接通过Task .Result属性来获取Task的结果
使用这种方式来获取结果,主线程会等待Task执行完成。也就是说,用这种方式来获取Task的返回结果,和不使用Task并没有什么区别。
但是需要注意的是,慎用.Result或者Wait来获取Task的返回值,除非你明确地知道Task的代码逻辑。我们来看下面一段代码
当点击button2时,应用程序会卡死。因为在调用.Result时,UI线程会阻塞,
而我们给GetResult的任务指出需要用UI线程来执行任务中的代码。
UI线程在等待GetResult完成,却又无法去运行GetResult中的代码。造成死锁
2.await 和 async
先上一个测试代码
static void Main(string[] args)
{
Console.WriteLine("程序启动");
Task<int> t = GetNum();
Console.WriteLine("主线程继续执行");
int num = t.Result;
Console.WriteLine("主线程获取到数字:" + num);
} public static async Task<int> GetNum()
{
Console.WriteLine("GetNum函数进入");
int num = await Task.Run(() => { Task.Delay(); return ; });
Console.WriteLine("GetNum函数获取到数字:"+num);
return num;
}
await 运算符只能用于异步方法中,所以包含await运算符的方法都需要有async修饰符来修饰,称之为异步方法。
通过实验,我们看到在异步函数中,遇到await运算符之后,主线程就继续往下执行了,更确切的解释是,Main函数开始和GetNum函数并行执行,
直到获取t.Result时,若GetNum()函数仍未执行完毕,Main函数则需要等待GetNum返回。
在GetNum函数中,await后面的代码需要等待await的Task执行完成后方可执行,等同于下面不适用await的代码
static void Main(string[] args)
{
Console.WriteLine("程序启动");
Task<int> t = GetNumNoAwait();
Console.WriteLine("主线程继续执行");
int num = t.Result;
Console.WriteLine("主线程获取到数字:" + num);
Console.Read();
} public static Task<int> GetNumNoAwait()
{
Console.WriteLine("GetNum函数进入");
Task<int> t = Task<int>.Run(() => { Task.Delay(); return ; });
t.ContinueWith(m => { Console.WriteLine("GetNum函数获取到数字:" + m.Result); });
return t;
}
通过await来获取返回值的操作,比前一种方式的优点在于他不会阻塞主线程的。这一点在winform或wpf等gui程序上可以很明显地提现出来
Task在winform中的使用
这是一个winform程序的代码片段,页面中有两个按钮,我们用maketext函数来模拟一个需要耗时的比如调用api获取数据的操作。
当点击button1时程序会一直等待结果返回,期间窗体无法拖动
而用异步方法则不会阻塞主窗体的其他操作
AsyncController
看过很多在Action中使用异步action的文章,并以此和未使用异步的Action来进行网站吞吐量的对比。
大概的代码类似于下面这样
最终都会得出一个结论,以上代码的吞吐量要远远高于未使用异步的
当时我就很不解,await就是在等待异步代码执行完成,并不会释放请求占用的线程,为什么会提升网站的吞吐量呢?
经过各种实验,仍然无法来证明以上代码的写法会使得网站的吞吐量更高,反而大多数情况下,效率稍微低了一些
(刚刚看过一本书中有介绍,通常的方法调用比使用async关键字的同样方法调用要快上40-50倍。所以异步函数在合适的场景被正确地使用也是非常重要的)
最终看了Msdn上关于异步控制器的介绍,方才找到正确的写法
以下是截取MSdn上的代码片段
首先使用 AsyncManager.OutstandingOperations.Increment()函数来设定未完成的请求操作,默认是1,然后每一个异步操作完成,通过Decrement来使计数器减1,当计数器归零之后,则会调用xxxCompleted函数来返回结果。
这样解释就行的通了,当执行完NewsAsync中的代码之后,请求线程就会释放,直到异步函数执行完成,系统会重新获取一个线程通过NewsCompleted来返回给客户端执行结果。
下面是我的测试代码
public class TaskTestController : AsyncController
{
public void GetDataAsync()
{
AsyncManager.OutstandingOperations.Increment();
WebClient client = new WebClient();
client.DownloadDataCompleted += (sender, e) =>
{
AsyncManager.Parameters["bytelength"] = e.Result.Length;
AsyncManager.OutstandingOperations.Decrement();
};
client.DownloadDataAsync(new Uri("https://docs.microsoft.com/zh-cn/dotnet/framework/get-started/"));
}
public int GetDataCompleted(int bytelength)
{
return bytelength;
}
}
public class TaskTest1Controller : Controller
{
public int GetData()
{
WebClient client = new WebClient();
var rsu = client.DownloadData(new Uri("https://docs.microsoft.com/zh-cn/dotnet/framework/get-started/"));
return rsu.Length; ;
}
}
然后我进行模拟1000个并发2000条请求,下面是测试结果
这里就可以看到异步控制器的优势已经显露出来了
然后我将iis的最大并发设置为10,模拟了一个20并发200条请求的操作,
异步控制器用时3.001s,失败0条
普通控制器用时4.551s,失败8条
测试完成,希望对有需要的人有所帮助
Task及Mvc的异步控制器 使用探索的更多相关文章
- ASP.NET MVC EF 中使用异步控制器
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 为什么使用异步操作/线程池 ASP.NET MVC ...
- ASP.NET MVC中使用异步控制器
线程池 一直想把项目改写成异步,但是ASP.NETMVC3下写的过于繁琐,.NET 4.5与ASP.NET MVC下代码写起来就比较简单了, MS好像也一直喜欢这样搞,每一个成熟的东西,都要演变好几个 ...
- ASP.NET MVC 学习笔记-6.异步控制器
1) 异步控制器的由来 对于IIS,它维护了一个.NET线程池来处理客户端请求,这个线程池称为工作线程池,其中的线程称为工作线程.当IIS接收到一个请求时,需要从工作线程池中唤醒一个工 ...
- c#异步编程(三)—ASP.NET MVC 异步控制器及EF异步操作
ASP.NET MVC 异步控制器及EF异步操作 异步控制器 ASP.NET MVC2后开始了对异步请求管道的支持,异步请求管道的作用是允许web服务器处理长时间运行的请求,比如 那些花费大量时间等待 ...
- 在 ASP.NET MVC 中使用异步控制器
线程池 一直想把项目改写成异步,但是ASP.NETMVC3下写的过于繁琐,.NET 4.5与ASP.NET MVC下代码写起来就比较简单了, MS好像也一直喜欢这样搞,每一个成熟的东西,都要演变好几个 ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- 看stackoverflow大牛如何回答何时在ASP.NET中使用异步控制器?
转载自博客园:http://farb.cnblogs.com/ 今天无意中看到stackoverflow上一个很好的问答,个人觉得很有价值,所以翻译过来和大家共享!希望大家能相互交流. 在ASP.NE ...
- MVC 4 异步编程简化了
MVC 3 异步编程好麻烦,需要使用异步控制器,一个Action需要拆成两个,很不方便.MVC3的好处是,只需要.NET Framework 4.0就能运行 MVC 4 之后只需要使用async和aw ...
- ASP.NET MVC学习之控制器篇扩展性
原文:ASP.NET MVC学习之控制器篇扩展性 一.前言 在之前的一篇随笔中已经讲述过控制器,而今天的随笔是作为之前的扩展. 二.正文 1.自定义动作方法 相信大家在开发过程一定会遇到动作方法的重名 ...
随机推荐
- Bear and Floodlight 状态压缩DP啊
Bear and Floodlight Time Limit: 4000MS Memory Limit: 262144KB 64bit IO Format: %I64d & %I64u ...
- asp.net core封装layui组件示例分享
用什么封装?自然是TagHelper啊,是啥?自己瞅文档去 在学习使用TagHelper的时候,最希望的就是能有个Demo能够让自己作为参考 怎么去封装一个组件? 不同的情况怎么去实现? 有没有更好更 ...
- Android ListView getViewTypeCount 的返回值问题解决
最近在学慕课网上的一个实战课程,期间有一个智能聊天机器人模块. 聊天界面通过 ListView 显示,用 Adapter 加载.一般来说,单对单的聊天,两者发出的话分别列在聊天页面的左右两边.所以,在 ...
- 【学习】条码扫描器:QuaggaJS
QuaggaJS是条形码扫描器完全用JavaScript编写,支持实时对各类条码进行定位和解码,如EAN和CODE128.该库还能够使用getUserMedia获得直接访问用户的摄像头流.为了充分利用 ...
- canvas图表详解系列(3):动态饼状图(原生Js仿echarts饼状图)
本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...
- JavaScript中的面向对象程序设计
本文内容目录顺序: 1.Object概念讲述: 2.面向对象程序设计特点: 3.JavaScript中类和实例对象的创建: 4.原型概念: 5.原型API: 6.原型对象的具体使用:7.深入理解使用原 ...
- win10 uwp 修改Pivot Header 颜色
我们在xaml创建一个Pivot <Pivot Grid.Row="1"> <PivotItem Header="lindexi">&l ...
- JPA + SpringData 操作数据库原来可以这么简单 ---- 深入了解 JPA - 1
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7703679.html ------------------------------------ ...
- C++点滴20130802
1.sprintf与printf,fprintf为三兄弟.其中printf输出到屏幕,fprintf输出到文件,而sprintf输出到字符串中.通常情况下,屏幕是可以输出的,文件也可以写的(除非磁盘满 ...
- flask + wtform + google storage
项目需要使用 flask 上传.下载文件到 google storage 上, 搜了一圈没有能直接结合 wtform 使用的插件,所以动手造了个轮子. 只实现了基本的上传,下载的功能,后续可能会完善预 ...