C# 1.0 特性之异步委托(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() 方法。

原文地址:https://www.cnblogs.com/abeam/p/11227923.html

时间: 2024-10-13 11:17:22

C# 1.0 特性之异步委托(AP、APM)的相关文章

C#基础之--线程、任务和同步:一、异步委托

创建线程的一种简单方式是定义一个委托,并异步调用它.委托是方法的类型安全的引用. Delegate还支持异步地调用方法.在后台Delegate类会创建一个执行任务的线程. 为了说明委托的异步特性,从一个需要一定的时间才能执行完毕的方法开始. TakesAWhile方法至少需要经过第2个变量传递的毫秒数才能执行完,因 为它调用了Thread.Sleep()方法. static int TakesAWhile(int data, int ms) { Console.WriteLine("TakesA

net .异步委托知识

以前在编程中,异步用的比较少,导致C# 一些基础的 东西用法都不怎么熟悉,经常要用的时候在去查找资料比较被动,而已没真正里面理解起来,始终感觉不是自己的知识 (题外话) 首先委托关键字  Delegate 1) 定义 /// 委托必须和要调用的异步方法有相同的签名 public delegate string AsyncMethodCaller(int callDuration, out int threadId); 2. 异步委托的方法 static string TestMethod(int

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

使用委托进行异步操作,处理一些耗时操作,防止主线程阻塞 使用例子: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Demo 7 { 8 class Program 9 { 10 11 static void Main(string[] args) 12 { 13 Func<string> fun = new Func<s

C# 多线程操作之异步委托

标签: 多线程任务nullstringhtml工作 2012-06-29 23:00 1276人阅读 评论(0) 收藏 举报  分类: C/C++/C#/dotnet(126)  目录(?)[+] 在应届生找工作的时候,多线程操作几乎是所有的公司都会问及的一个基本问题. 这里做了一个多线程操作的总结,这里总结了通过异步委托来实现多线程操作. 定义一个委托,是创建一个线程的最简单的方法,并且异步调用它.委托是方法的类型安全的引用.同时委托还智齿异步调用方法. 委托使用线程池来完成异步任务. 当自己

委托、泛型委托、异步委托(新人,有问题请指出,有部分代码是拿其他博客主的)

委托 是把一个方法当作一个参数放到声明(deletgate)委托中.给另一个方法时候,或者直接执行委托. 1 class Delegates 2 { 3 public delegate void mydelegate(string msg); 4 static void Main(string[] args) 5 { 6 mydelegate myd = new mydelegate(father.BuyToys); 7 myd("BBB"); 8 Console.Read(); 9

.NET委托解析(异步委托)

上一篇我们了解到了,委托的基本感念,列举了几个委托的实例,并根据实例来反编译源码查看.NET 委托的内部实现,从浅入深的角度来详细的去解析委托的实质,本文将系上篇继续讨论异步委托的实现以及异步委托的源码解析. 首先本文只会从委托的层面的去编写,不会涉及到深层次的异步.(后续的系列中将会对异步进行深入讲解.敬请关注.). 委托的异步调用方式 在上一篇中我们在实例中是直接通过委托对象的,例如: private static void Main(string[] args)  {        Pro

关于servlet3.0中的异步servlet

刚看了一下维基百科上的介绍,servlet3.0是2009年随着JavaEE6.0发布的: 到现在已经有六七年的时间了,在我第一次接触java的时候(2011年),servlet3.0就已经出现很久了,但是到现在,里边的一些东西还是没有能够好好地了解一下 最近在研究java的长连接,在了解jetty中的continuations机制的时候也重新了解了一下servlet3.0中的异步servlet机制,通过看几个博客,加上自己的一些测试,算是搞明白了一些,在这里记录一下: 在服务器的并发请求数量比

MVC 5.0(or5.0↓) Ajax.BeginForm 异步上传附件问题,答案是不能的!

MVC 5.0(or5.0↓)  Ajax.BeginForm 异步上传附件问题,答案是不能的! (请注意我这里说的异步!) 来看一下下面这段一步提交file的代码 //前台 .cshtml 文件 <script src="~/jquery.unobtrusive-ajax.js"></script>@using (Ajax.BeginForm("upLoadAsync", "UploadFile", new AjaxOp

异步委托

委托实现多窗体传值 主窗体的代码: public partial class FrmMain : Form { //[3] 创建委托对象(委托对象能够将委托和具体方法关联) public ShowCounterDelegate msgSender; public FrmMain() { InitializeComponent(); //创建从窗体对象 FrmOther01 objFrm01 = new FrmOther01(); FrmOther02 objFrm02 = new FrmOthe