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

Wesley13
• 阅读 801

Ø 前言

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. 【推荐】使用异步回调委托的方式,当异步委托方法执行完成后调用,如果在不需要非要等到异步完成时获取返回结果的情况下,推荐使用该方式。

  5. 下面分别使用这四种方式等待

首先,定义四个委托类型。

public delegate void MyDelegate1();

public delegate string MyDelegate2();

public delegate void MyDelegate3(string str);

public delegate int MyDelegate4(int num1, int num2);

  1. 使用 WaitOne() 方法(匿名方法)

///

/// 使用 WaitOne() 方法(匿名方法)。

///

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());

}

运行以上代码:

[C# 1.0 新特性之异步委托(AP、APM) ](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F654920%2F201907%2F654920-20190722193033989-1820431217.jpg)

  1. 使用委托对象的 EndInvoke() 方法(匿名方法)

///

/// 使用委托对象的 EndInvoke() 方法(匿名方法)。

///

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);

}

运行以上代码:

[C# 1.0 新特性之异步委托(AP、APM) ](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F654920%2F201907%2F654920-20190722193048829-298476866.jpg)

  1. 使用 IAsyncResult.IsCompleted 属性(Lambda 表达式)

///

/// 使用 IAsyncResult.IsCompleted 属性(Lambda 表达式)。

///

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());

}

运行以上代码:

[C# 1.0 新特性之异步委托(AP、APM) ](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F654920%2F201907%2F654920-20190722193100878-241942199.png)

  1. 【推荐】使用异步回调委托

///

/// 【推荐】使用异步回调委托。

///

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());

}

运行以上代码:

[C# 1.0 新特性之异步委托(AP、APM) ](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F654920%2F201907%2F654920-20190722193114440-728787529.jpg)

  1. 下面,再来看下 C# 中一些常用基于异步回调的运用
  1. 模拟 Web 请求,异步读取响应流

///

/// 异步获取网页 HTML 内容。

///

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);

}

}

}

下面是两种读取方式的结果:

[C# 1.0 新特性之异步委托(AP、APM) ](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F654920%2F201907%2F654920-20190722193133141-1468719067.jpg)

[C# 1.0 新特性之异步委托(AP、APM) ](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fimg2018.cnblogs.com%2Fblog%2F654920%2F201907%2F654920-20190722193145581-586785963.jpg)

Ø 总结

  1. 异步委托主要使用 BeginInvoke() 方法开启异步委托,该方法传入一个回调委托 AsyncCallback 对象。

  2. BeginInvoke() 返回一个实现了 IAsyncResult 接口的对象,可以使用该对象的 AsyncWaitHandle.WaitOne() 方法和 IsCompleted 属性判断异步是否完成。

  3. 同样 AsyncCallback 委托的签名也有个 IAsyncResult 参数,该委托将在异步调用完成时执行。

  4. 需要获取异步委托的返回结果,都必须调用 EndInvoke() 方法。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这