Ø  前言

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)的更多相关文章

  1. C# 1.0 新特性之异步委托(AP、APM)

    Ø  前言 C# 异步委托也是属于异步编程中的一种,可以称为 Asynchronous Programming(异步编程)或者 Asynchronous Programming Model(异步编程模 ...

  2. 异步委托(APM)使用Func异步操作,处理耗时操作

    使用委托进行异步操作,处理一些耗时操作,防止主线程阻塞 使用例子: using System; using System.Collections.Generic; using System.Linq; ...

  3. C# 异步编程1 APM模式异步程序开发

    C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程.广告一下:喜欢我文章的朋友,请 ...

  4. C# 异步编程1 APM 异步程序开发

    C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程.广告一下:喜欢我文章的朋友,请 ...

  5. 转:[你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中介绍了从C#1.0——C#4.0中一些 ...

  6. [你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中 介绍了从C#1.0——C#4.0中一 ...

  7. 一、异步编程模型(APM)

    一.概念 APM即异步编程模式的简写(Asynchronous Programming Model).大家在写代码的时候或者查看.NET 的类库的时候肯定会经常看到和使用以BeginXXX和EndXX ...

  8. .NET异步编程之APM模式

    目录 1.AMP模式简介 2.使用BeginInvoke实现异步委托 3.原始线程怎么知道新线程已经运行完毕 4.使用AsyncCallback委托实现回调模式 5.源代码下载 shanzm-2020 ...

  9. C#固定时间执行指定事件(观察者模式+异步委托)

    最近有个项目需要每天固定的时间去执行指定的事件,发现网上关于这样的文章比较少,而且比较散.通过学习了几篇文章后终于实现了这个功能,在此也特别感谢这些文章的作者们,这也是我第一次在园子里面发文章,望多指 ...

随机推荐

  1. Crash 的文明世界

    题目描述 给一棵树,求以每个点为根时下列式子的值. 题解 当k=1时这就是一个经典的换根dp问题. 所以这道题还是要用换根dp解决. 部分分做法: 考虑转移时是这样的一个形式(图是抄的). 用二项式定 ...

  2. [NOI2005]月下柠檬树(计算几何+积分)

    题目描述 李哲非常非常喜欢柠檬树,特别是在静静的夜晚,当天空中有一弯明月温柔 地照亮地面上的景物时,他必会悠闲地坐在他亲手植下的那棵柠檬树旁,独自思 索着人生的哲理. 李哲是一个喜爱思考的孩子,当他看 ...

  3. A/D和D/A的学习

    从我们学到的知识了解到,我们的单片机是一个典型的数字系统.数字系统只能对输入的数字信号进行处理,其输出信号也是数字信号.但是在工业检测系统和日常生活中的许多物理量都是模拟量,比如温度.长度.压力.速度 ...

  4. CodeForces - 589J(DFS)

    题目链接:http://codeforces.com/problemset/problem/589/J 题目大意:一个机器人打扫一个密闭的房间,房间由一个矩形构成,'*'表示家具,'.'表示该位置为空 ...

  5. 使用Docker Swarm搭建分布式爬虫集群

    https://mp.weixin.qq.com/s?__biz=MzIxMjE5MTE1Nw==&mid=2653195618&idx=2&sn=b7e992da6bd1b2 ...

  6. js定时器setInterval()与setTimeout()

    js定时器setInterval()与setTimeout() 1.setTimeout(Expression,DelayTime),在DelayTime过后,将执行一次Expression,setT ...

  7. 解题:CF622F The Sum of the k-th Powers

    题面 TJOI2018出CF原题弱化版是不是有点太过分了?对,就是 TJOI2018 教科书般的亵渎 然而我这个问题只会那个题的范围的m^3做法 回忆一下1到n求和是二次的,平方求和公式是三次的,立方 ...

  8. tyvj/joyoi 1305 最大子序和

    带了一个转化的单调队列裸题. 转化为前缀和相减即可. 有一点需要注意:从0开始入队而不是1,因为要统计第一个. (从网上找的对拍程序,结果别人写错了) /** freopen("in.in& ...

  9. vue2.0项目实战(4)生命周期和钩子函数详解

    最近的项目都使用vue2.0来开发,不得不说,vue真的非常好用,大大减少了项目的开发周期.在踩坑的过程中,因为对vue的生命周期不是特别了解,所以有时候会在几个钩子函数里做一些事情,什么时候做,在哪 ...

  10. 腾讯云centos7安装MySQL

    centos就centos呗,为什么要加个腾讯云呢?有这种疑问的兄dei,一定是没被不同云的系统坑过啊,阿里云的Ubuntu和腾讯云的Ubuntu不一样,centos好像也有差别,各个云平台,同样的系 ...