《CLR Via C# 第3版》笔记之(二十一) - 异步编程模型(APM)
APM是.NET中异步编程模型的缩写(Asynchronous Programing Model)。
通过异步编程,使得我们的程序可以更加高效的利用系统资源。
主要内容:
- 一个APM的例子
- GUI中的APM
- APM的优劣点
- AMP使用中的注意事项
1. 一个APM的例子
.Net中的异步模型非常完善,只要看到Begin***者End***方法。基本都是相对***方法的异步调用方式。
(注:***是方法的名称)
所以在.Net中实现一个异步调用是很方便的,下面用个小例子来演示一个异步操作。
首先是同步的方式请求百度搜索10次。(分别搜索1,2,3。。。。10)
public class CLRviaCSharp_21
{static void Main(string[] args){// 用百度分别检索1,2,3。。。9,共检索10次DateTime start = DateTime.Now;string strReq = "http://www.baidu.com/s?wd={0}";for (int i = 0; i < 10; i++){var req = WebRequest.Create(string.Format(strReq, i));// 注意这里的GetResponse是同步方法var res = req.GetResponse();Console.WriteLine("检索 {0} 的结果已经返回!", i);res.Close();}Console.WriteLine("耗用时间:{0}毫秒", TimeSpan.FromTicks(DateTime.Now.Ticks - start.Ticks).TotalMilliseconds);Console.ReadKey(true);}
}
结果如下:总共消耗时间33秒多。
异步的方式如下:
public class CLRviaCSharp_21
{static DateTime start;static void Main(string[] args){// 用百度分别检索1,2,3。。。9,共检索10次start = DateTime.Now;string strReq = "http://www.baidu.com/s?wd={0}";for (int i = 0; i < 10; i++){var req = WebRequest.Create(string.Format(strReq, i));// 注意这里的BeginGetResponse就是异步方法var res = req.BeginGetResponse(ProcessWebResponse, req);}Console.ReadKey(true);}private static void ProcessWebResponse(IAsyncResult result){var req = (WebRequest)result.AsyncState;string strReq = req.RequestUri.AbsoluteUri;using (var res = req.EndGetResponse(result)){Console.Write("检索 {0} 的结果已经返回!\t", strReq.Substring(strReq.Length - 1));Console.WriteLine("耗用时间:{0}毫秒", TimeSpan.FromTicks(DateTime.Now.Ticks - start.Ticks).TotalMilliseconds);}}}
结果如下:总共消耗的时间应该是下面所有时间中最长的那个,即9.5秒左右。
2. GUI中的APM
异步编程除了在服务端会大量应用,在有GUI的客户端也应用比较多(为了保证客户端的界面不会假死)。
但是Winform或WPF程序中,改变界面元素状态只有通过UI线程,其他线程如果试图改变UI元素,就会抛出异常(System.InvalidOperationException)。
using System;
using System.Windows.Forms;
using System.Net;public class CLRviaCSharp_21
{static void Main(string[] args){MyWindowsForm mf = new MyWindowsForm();mf.ShowDialog();}
}internal class MyWindowsForm : Form
{public MyWindowsForm(){Text = "Click in the window to start a web request";Width = 800;Height = 600;}protected override void OnMouseClick(MouseEventArgs e){// 开始异步web请求Text = "web request initilized";var webreq = WebRequest.Create("http://www.baidu.com");webreq.BeginGetResponse(ProcessWebResponse, webreq);base.OnMouseClick(e);}private void ProcessWebResponse(IAsyncResult result){var req = (WebRequest)result.AsyncState;using (var res = req.EndGetResponse(result)){// SynchronizationContext.Current.Post(updateUI, res);// 这里改变UI元素Form的Text属性,抛出了异常Text = "Content length: " + res.ContentLength;}}
}
那么其他线程如何改变UI元素呢。为了保证UI始终有响应,必须能够让其他线程也能改变UI元素。
答案就在上面代码中抛异常的上面一句代码。利用SynchronizationContext的Current属性来获取UI线程的同步上下文信息。
然后调用其Post方法,异步的更新UI元素。
using System;
using System.Windows.Forms;
using System.Net;
using System.Threading;public class CLRviaCSharp_21
{static void Main(string[] args){MyWindowsForm mf = new MyWindowsForm();mf.ShowDialog();}
}internal class MyWindowsForm : Form
{public MyWindowsForm(){Text = "Click in the window to start a web request";Width = 800;Height = 600;}protected override void OnMouseClick(MouseEventArgs e){// 开始异步web请求Text = "web request initilized";var webreq = WebRequest.Create("http://www.baidu.com");webreq.BeginGetResponse(ProcessWebResponse, webreq);base.OnMouseClick(e);}private void ProcessWebResponse(IAsyncResult result){var req = (WebRequest)result.AsyncState;using (var res = req.EndGetResponse(result)){SynchronizationContext.Current.Post(updateUI, res);}}private void updateUI(object state){var res = (WebResponse)state;// 这里改变UI元素Form的Text属性, updateUI这个函数是由UI线程执行的Text = "Content length: " + res.ContentLength;}
}
这样的实现看着很繁琐,所以《CLR via C#》作者Jeffrey实现了一个小方法(SyncContextCallback)来简化这个步骤。
using System;
using System.Windows.Forms;
using System.Net;
using System.Threading;public class CLRviaCSharp_21
{static void Main(string[] args){MyWindowsForm mf = new MyWindowsForm();mf.ShowDialog();}
}internal class MyWindowsForm : Form
{public MyWindowsForm(){Text = "Click in the window to start a web request";Width = 800;Height = 600;}protected override void OnMouseClick(MouseEventArgs e){// 开始异步web请求Text = "web request initilized";var webreq = WebRequest.Create("http://www.baidu.com");webreq.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webreq);base.OnMouseClick(e);}private void ProcessWebResponse(IAsyncResult result){var req = (WebRequest)result.AsyncState;using (var res = req.EndGetResponse(result)){// 这里改变UI元素Form的Text属性, // 这次是在UI线程执行的,SyncContextCallback中获取了UI线程的同步上下文,// 然后用Post方法调用了ProcessWebResponse函数Text = "Content length: " + res.ContentLength;}}// 大牛Jeffrey实现的SyncContextCallback方法private static AsyncCallback SyncContextCallback(AsyncCallback callback){SynchronizationContext sc = SynchronizationContext.Current;// 如果没有同步上下文,直接返回传入的东西if (sc == null) return callback;// 返回一个委托,这个委托将委托Post到捕捉到的SC中return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);}
}
3. APM的优劣点
上面两个例子,可以看出APM的一些优势,第一个例子说明异步的效率高。第二个GUI的例子说明异步可以增强用户体验。
其实APM的优势总结起来有以下几点:
- 资源利用率底(比如I/O并发读写时,线程不必等I/O完了就可以处理其他请求。这样几个线程就可以处理大量请求)
- 垃圾回收快(线程不用等待请求的操作完成就回线程池等待了,这时的线程在它们的栈顶,垃圾回收时遍历线程查找根比较快)
- 线程少,调试快(调试时,一旦遇到断点就会挂起所有线程。异步可以保证线程尽量少,所以调试时感觉会快)
- 使得GUI一直保持响应(上面的GUI例子可以说明)
- 并发执行下载,速度快(上面第一个例子可以说明)
当然,任何技术都不是完美的,有其适用的一面,也有其不适用的一面。
了解它的缺点,有时候甚至可以帮助我们更好的使用它。
APM的劣势主要有以下几点:
- 必须将代码分成多个回调方法(执行流程不直观)
- 避免使用实参和局部变量(实参和局部变量在回调方法中无法访问,记住回调方法在另一个线程中)
- 许多C#构造无法使用,比如try/catch/finally,using等(因为没法在一个方法中开始try,再在它的回调方法中完成finally)
- 有些功能很难实现,比如多个并发操作协作进行,取消和超时(比如上面例子中的更新GUI元素也很麻烦)
4. AMP使用中的注意事项
使用APM时以下几点需要注意:
- 不要提早调用End***方法,在回调函数中再调用End***(如果提早调用,异步操作没有完成,End***会一直等待,此时相当于同步调用)
- 调用End***且仅调用一次(异步操作初始化时分配的某些资源,必须调用End***才能释放。第二次调用End***时结果不可预测)
- 使用相同的对象调用Begin***方法和End***方法
- 在Begin***方法和End***方法中使用ref,out,params标记时需要注意(具体实现方法以后有空再补些例子:))
- 不能取消异步I/O操作(启动I/O操作后,控制权交给了I/O相关硬件)
- Begin***方法返回引用类型,无法返回值类型
- 用于打开文件的Win32 API(CreateFile)暂时没有异步的版本
- FileStream类在创建时就决定了是异步还是同步的方式(创建式指定异步标记,即使调用Read,也是通过Sleep来模拟同步。同样,指定了同步标记,调用BeginRead也是模拟的异步)
转载于:https://www.cnblogs.com/wang_yb/archive/2011/11/29/2267790.html
《CLR Via C# 第3版》笔记之(二十一) - 异步编程模型(APM)相关推荐
- Android 学习之《第一行代码》第二版 笔记(二十三)Material Design 实战 —— 卡片式布局
实现基础: Android 学习之<第一行代码>第二版 笔记(二十二)Material Design 实战 -- 悬浮按钮和可交互提示 卡片式布局 卡片式布局是 Materials Des ...
- 《Java8实战》读书笔记10:组合式异步编程 CompletableFuture
<Java8实战>读书笔记10:组合式异步编程 CompletableFuture 第11章 CompletableFuture:组合式异步编程 11.1 Future 接口 (只是个引子 ...
- OpenCV学习笔记(二十一)——绘图函数core OpenCV学习笔记(二十二)——粒子滤波跟踪方法 OpenCV学习笔记(二十三)——OpenCV的GUI之凤凰涅槃Qt OpenCV学习笔记(二十
OpenCV学习笔记(二十一)--绘图函数core 在图像中,我们经常想要在图像中做一些标识记号,这就需要绘图函数.OpenCV虽然没有太优秀的GUI,但在绘图方面还是做得很完整的.这里就介绍一下相关 ...
- Slicer学习笔记(二十一)slicer的python接口说明
Slicer学习笔记(二十一)slicer的python接口说明 1.python接口 1.python接口 slicer package Submodules slicer.ScriptedLoad ...
- ElasticSearch学习笔记之二十一 指标聚合
ElasticSearch学习笔记之二十一 指标聚合 指标聚合 Avg Aggregation Script Value Script Missing value Weighted Avg Aggre ...
- VTK学习笔记(二十一)vtk裁剪求截面面积
VTK学习笔记(二十一)vtk裁剪求界面面积 1.代码 2.CMakeLists.txt 3.运行输出 4.面积正确性验证 4.1.代码 4.2.执行结果 1.代码 #pragma once#incl ...
- bootstrap媒体查询类型的值_HTMLCSS学习笔记(二十一)-- 媒体查询 + rem用法
媒体查询 + rem 计算方法 计算rem方法: 结合媒体查询 -> 随着设备的改变 更改html font-size的值. 媒体查询确定范围?? 移动端设计图 : 640px 750p ...
- 机器学习-白板推导系列笔记(二十一)-RBM
此文章主要是结合哔站shuhuai008大佬的白板推导视频:受限玻尔兹曼机_155min 全部笔记的汇总贴:机器学习-白板推导系列笔记 玻尔兹曼机介绍:白板推导系列笔记(二十八)-玻尔兹曼机 一.背景 ...
- NET CLR via c# 第4版笔记 第19章 可空值类型
System.Nullable<T> 是结构. 19.1 C# 对可空值类型的支持 C# 允许用问号表示法来声明可空值类型,如: Int32? x = 5;Int32? y = null; ...
最新文章
- 轻松恢复误删除的共享文件,DPM2007系列之六
- Ubuntu下找不到ttyUSB*问题解决
- 全球移动SaaS市场规模5年将增170亿美元
- MATLAB Simulink 做BP PID报错:Error :*** during flag=* call must be a real vector of length 3
- ubuntu无法安装vscode(visual studio code)如何卸载snap?
- servlet的由来
- 【报告分享】2020中国教育行业生存实录.pdf(附下载链接)
- D. Beautiful numbers
- python 字符串子串_Python字符串子字符串
- php事务基本要素,数据库事务正确执行的四个基本要素
- 使用SQL2005 递归查询结合Row_Number()实现完全SQL端树排序
- ios十进制、十六进制字符串,byte,data等之间的转换
- 8253/8255/8259相关知识
- windows快速全局检索文件工具-Listary
- APUE代码运行环境的搭建
- 招聘中的热门技术技能:SQL、Java、Python 和 Linux
- android京东源码下载,京东商城APP - 源码下载|通讯/手机编程|android开发|源代码 - 源码中国...
- vue:无法加载文件..
- 南京计算机图书,计算机中心附近图书馆
- PS绘画效果滤镜Snap Art 4