C# 异步委托(AP、APM)
Ø 前言
C# 异步委托也是属于异步编程中的一种,可以称为 Asynchronous Programming(异步编程)或者 Asynchronous Programming Model(异步编程模型),因为这是实现异步编程的模式。委托是 C#1.0 就有的特性,并且 .NET v1.0 同时也伴随有 AsyncCallback、IAsyncResult 等类/接口的出现,所以所有的 .NET 版本中都是支持的。
1. 什么是异步委托
1) 异步委托是采用异步回调的方式实现异步执行,当使用委托异步执行某个方法时,将从线程池中取出一个线程去执行该方法。
2) 当执行完成后则调用 AsyncCallback 委托指定的方法,完成异步回调。
3) 开始执行一个异步委托后,可以使用4种方式等待异步执行完成:
1. 开启异步委托后,BeginInvoke() 方法将返回一个实现了 IAsyncResult 接口的 System.Runtime.Remoting.Messaging.AsyncResult 对象。使用该对象的 AsyncWaitHandle 属性,并调用 WaitOne() 方法,该方法会阻塞当前线程,直到收到信号(异步委托方法执行完成)。
2. 调用委托对象的 EndInvoke() 方法,需要传递一个 AsyncResult 对象,该方法也用于获取异步委托的返回值,所以这种方式也会阻塞当前线程。
3. 使用 IAsyncResult.IsCompleted 属性,判断是否执行完成。该属性在异步委托方法执行完成时为 true.
4. 【推荐】使用异步回调委托的方式,当异步委托方法执行完成后调用,如果在不需要非要等到异步完成时获取返回结果的情况下,推荐使用该方式。
2. 下面分别使用这四种方式等待
首先,定义四个委托类型。
public delegate void MyDelegate1();
public delegate string MyDelegate2();
public delegate void MyDelegate3(string str);
public delegate int MyDelegate4(int num1, int num2);
1) 使用 WaitOne() 方法(匿名方法)
/// <summary>
/// 使用 WaitOne() 方法(匿名方法)。
/// </summary>
public void AsyncDelegateTest1()
{
WriteLine("AsyncDelegateTest1() 方法开始执行,线程Id:{0}", GetThreadId());
MyDelegate1 d1 = new MyDelegate1(delegate()
{
WriteLine("匿名方法开始执行,线程Id:{0},{1}", GetThreadId(), GetTime());
Thread.Sleep(3000);
WriteLine("匿名方法结束执行,线程Id:{0}", GetThreadId());
});
IAsyncResult ar = d1.BeginInvoke(null, null);
ar.AsyncWaitHandle.WaitOne(); //这里将阻塞线程,直到收到信号(异步方法执行完成)
WriteLine("AsyncDelegateTest1() 方法结束执行,线程Id:{0},{1}", GetThreadId(), GetTime());
}
运行以上代码:

2) 使用委托对象的 EndInvoke() 方法(匿名方法)
/// <summary>
/// 使用委托对象的 EndInvoke() 方法(匿名方法)。
/// </summary>
public void AsyncDelegateTest2()
{
WriteLine("AsyncDelegateTest2() 方法开始执行,线程Id:{0}", GetThreadId());
MyDelegate2 d2 = new MyDelegate2(delegate()
{
WriteLine("匿名方法开始执行,线程Id:{0},{1}", GetThreadId(), GetTime());
Thread.Sleep(3000);
WriteLine("匿名方法结束执行,线程Id:{0}", GetThreadId());
return System.Reflection.MethodBase.GetCurrentMethod().Name;
});
IAsyncResult ar = d2.BeginInvoke(null, null);
string result = d2.EndInvoke(ar); //这里也将阻塞线程,直到异步方法执行完成
WriteLine("AsyncDelegateTest2() 方法结束执行,{0},异步委托返回结果:{1}", GetTime(), result);
}
运行以上代码:

3) 使用 IAsyncResult.IsCompleted 属性(Lambda 表达式)
/// <summary>
/// 使用 IAsyncResult.IsCompleted 属性(Lambda 表达式)。
/// </summary>
public void AsyncDelegateTest3()
{
WriteLine("AsyncDelegateTest3() 方法开始执行,线程Id:{0}", GetThreadId());
MyDelegate3 d3 = new MyDelegate3((str) =>
{
WriteLine("Lambda 表达式开始执行,线程Id:{0},{1}", GetThreadId(), GetTime());
Thread.Sleep(3000);
WriteLine("Lambda 表达式结束执行,str:{0}", str);
});
IAsyncResult ar = d3.BeginInvoke("这是一段话!", null, null);
while (!ar.IsCompleted) //标记是否完成(其实与直接调 EndInvoke() 方法没什么区别)
{ }
WriteLine("AsyncDelegateTest3() 方法结束执行,线程Id:{0},{1}", GetThreadId(), GetTime());
}
运行以上代码:

4) 【推荐】使用异步回调委托
/// <summary>
/// 【推荐】使用异步回调委托。
/// </summary>
public void AsyncDelegateTest4()
{
WriteLine("AsyncDelegateTest4() 方法开始执行,线程Id:{0}", GetThreadId());
MyDelegate4 d4 = new MyDelegate4(Add);
//这里必须将第二个参数(委托对象)传入,否则异步回调中 IAsyncResult.AsyncState 属性将为 null.
IAsyncResult ar = d4.BeginInvoke(22, 36, new AsyncCallback(AddCallback), d4);
WriteLine("AsyncDelegateTest4() 方法结束执行,线程Id:{0}", GetThreadId());
}
public int Add(int num1, int num2)
{
WriteLine("Add() 方法开始执行,线程Id:{0},{1}", GetThreadId(), GetTime());
Thread.Sleep(3000);
WriteLine("Add() 方法结束执行,线程Id:{0}", GetThreadId());
return num1 + num2;
}
public void AddCallback(IAsyncResult ar)
{
WriteLine("AddCallback() 方法开始执行,线程Id:{0},{1}", GetThreadId(), GetTime());
MyDelegate4 d4 = ar.AsyncState as MyDelegate4; //获取委托对象
int result = d4.EndInvoke(ar); //这里并不会阻塞
WriteLine("AddCallback() 方法结束执行,计算结果:{0},{1}", result, GetTime());
}
运行以上代码:

3. 下面,再来看下 C# 中一些常用基于异步回调的运用
1) 模拟 Web 请求,异步读取响应流
/// <summary>
/// 异步获取网页 HTML 内容。
/// </summary>
public void AsyncGetHtmlString()
{
WriteLine("AsyncGetHtmlString() 方法开始执行,线程Id:{0},{1}", GetThreadId(), GetTime());
WebRequest request = WebRequest.Create("http://www.cnblogs.com/abeam/");
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
WriteLine("AsyncGetHtmlString() 方法结束执行,线程Id:{0}", GetThreadId());
}
public async void ResponseCallback(IAsyncResult ar)
{
WriteLine("ResponseCallback() 方法开始执行(此时已经获得响应),线程Id:{0},{1}", GetThreadId(), GetTime());
WebRequest request = ar.AsyncState as WebRequest;
using (WebResponse response = request.EndGetResponse(ar))
{
using (var stream = response.GetResponseStream())
{
WriteLine("开始异步读取,线程Id:{0},{1}", GetThreadId(), GetTime());
WriteLine("响应的 HTML 内容:");
int count, totalCount = 0;
//1. 同步读取响应流
using (var sr = new System.IO.StreamReader(stream, Encoding.UTF8))
{
char[] chars = new char[256];
while ((count = sr.Read(chars, 0, chars.Length)) > 0)
{
totalCount += count;
if (totalCount <= chars.Length) //太多屏幕容不下
{
string content = new string(chars, 0, count);
WriteLine(content);
WriteLine("同步读取流线程Id:{0}", GetThreadId());
}
}
}
WriteLine("响应的 HTML 总字符数:{0}", totalCount);
//2. 异步读取响应流
/*
* byte[] buffer = new byte[stream.Length];
* int totalCount = await stream.ReadAsync(buffer, 0, buffer.Length);
* 不能使用 stream.Length,因为 stream 是一种 System.Net.ConnectStream,否者将报异常:
* 未处理System.NotSupportedException
* Message: “System.NotSupportedException”类型的未经处理的异常在 mscorlib.dll 中发生
* 其他信息: 此流不支持查找操作。
*/
byte[] buffer = new byte[1024];
while ((count = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
totalCount += count;
if (totalCount <= 1024) //太多屏幕容不下
{
string content = Encoding.UTF8.GetString(buffer);
Write(content);
}
WriteLine();
Write("异步读取流线程Id:{0}", GetThreadId());
}
WriteLine();
WriteLine("响应的 HTML 总字节数:{0}", totalCount);
}
}
}
下面是两种读取方式的结果:


Ø 总结
1. 异步委托主要使用 BeginInvoke() 方法开启异步委托,该方法传入一个回调委托 AsyncCallback 对象。
2. BeginInvoke() 返回一个实现了 IAsyncResult 接口的对象,可以使用该对象的 AsyncWaitHandle.WaitOne() 方法和 IsCompleted 属性判断异步是否完成。
3. 同样 AsyncCallback 委托的签名也有个 IAsyncResult 参数,该委托将在异步调用完成时执行。
4. 需要获取异步委托的返回结果,都必须调用 EndInvoke() 方法。
C# 异步委托(AP、APM)的更多相关文章
- C# 1.0 新特性之异步委托(AP、APM)
Ø 前言 C# 异步委托也是属于异步编程中的一种,可以称为 Asynchronous Programming(异步编程)或者 Asynchronous Programming Model(异步编程模 ...
- 异步委托(APM)使用Func异步操作,处理耗时操作
使用委托进行异步操作,处理一些耗时操作,防止主线程阻塞 使用例子: using System; using System.Collections.Generic; using System.Linq; ...
- C# 异步编程1 APM模式异步程序开发
C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程.广告一下:喜欢我文章的朋友,请 ...
- C# 异步编程1 APM 异步程序开发
C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程.广告一下:喜欢我文章的朋友,请 ...
- 转:[你必须知道的异步编程]——异步编程模型(APM)
本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中介绍了从C#1.0——C#4.0中一些 ...
- [你必须知道的异步编程]——异步编程模型(APM)
本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中 介绍了从C#1.0——C#4.0中一 ...
- 一、异步编程模型(APM)
一.概念 APM即异步编程模式的简写(Asynchronous Programming Model).大家在写代码的时候或者查看.NET 的类库的时候肯定会经常看到和使用以BeginXXX和EndXX ...
- .NET异步编程之APM模式
目录 1.AMP模式简介 2.使用BeginInvoke实现异步委托 3.原始线程怎么知道新线程已经运行完毕 4.使用AsyncCallback委托实现回调模式 5.源代码下载 shanzm-2020 ...
- C#固定时间执行指定事件(观察者模式+异步委托)
最近有个项目需要每天固定的时间去执行指定的事件,发现网上关于这样的文章比较少,而且比较散.通过学习了几篇文章后终于实现了这个功能,在此也特别感谢这些文章的作者们,这也是我第一次在园子里面发文章,望多指 ...
随机推荐
- 「HNOI2016」树 解题报告
「HNOI2016」树 事毒瘤题... 我一开始以为每次把大树的子树再接给大树,然后死活不知道咋做,心想怕不是个神仙题哦 然后看题解后才发现是把模板树的子树给大树,虽然思维上难度没啥了,但是还是很难写 ...
- luogu3250 网络 (整体二分+树上差分+树状数组)
首先整体二分,问题变成是否存在经过一个点的满足条件的路径 那么我对于每个路径(a,b,lca),在树状数组的dfn[a]++,dfn[b]++,dfn[lca]--,dfn[fa[lca]--] 然后 ...
- poj2524(并查集水题)
题目链接:http://poj.org/problem?id=2524 题目大意:学校共有n个同学,告诉你m对同学信仰同一宗教,问这个学校学生信仰宗教的数目最多为多少. 例: Sample Input ...
- JS小积累(二)— 自动获取浏览器尺寸
JS小积累-获取浏览器窗口尺寸 作者: 狐狸家的鱼 GitHub:八至 autodivheight(); function autodivheight() { //函数:获取尺寸 //获取浏览器窗口高 ...
- JS小积累(一)— 判断在线离线
JS小积累-判断在线离线 作者: 狐狸家的鱼 Github: 八至 if(window.navigator.onLine==true){ console.log('online'); ... } el ...
- 洛谷P3338 力
题意: 解: 介绍两种方法. 首先可以把那个最后除的qi拆掉. ①分前后两部分处理. 前一部分可以看做是个卷积.下面的平方不拆开,直接看成gi-j即可. 后一部分按照套路,把循环变量改成从0开始,反转 ...
- n+lognlogV查找最大值
来自Blogewoosh #6. 啃了一下,写个翻译吧. 问题:你有一个数组,你不知道每个元素的大小,但是能够提出询问:a[x]是否>=v?你需要找出这个数组的最大值,只能询问n + lognl ...
- 如何使用Senparc.Weixin SDK 底层的Redis缓存并设置过期时间
最近在微信第三方平台项目开发中,有一个需求,所有绑定的公众号的回复规则按照主公众号的关键词配置来处理,我的处理思路是获取主公众号配置的关键词回复规则,缓存10分钟,由于需要使用Redis缓存来存储一些 ...
- (转)轻松学,Java 中的代理模式及动态代理
背景:讲到反射机制,肯定会想到动态代理. 轻松学,Java 中的代理模式及动态代理 代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强.值得注意的是,代理类和被代理类应该 ...
- C语言进阶——Day 1
C语言提高笔记 Day 1 小数据赋给大变量,首位是1则在前面自动补充1,首位是0则在前方自动补充0. 大数据赋给小变量,低位字节对齐,truncate截断,有可能会造成数据丢失. 程序和进程的差别: ...