.NET中的异步编程-EAP/APM使用方法及案例介绍

从.NET 4.5开始,支持的三种异步编程模式
•基于事件的异步编程设计模式 (EAP,Event-based Asynchronous Pattern)
•异步编程模型(APM,Asynchronous Programming Model)
•基于任务的编程模型(TAP,Task-based Asynchronous Pattern)
基于任务的异步模式 (TAP) 是基于 System.Threading.Tasks 命名空间的 Task 和 Task<TResult>,用于表示任意异步操作。 TAP 是新开发的建议异步设计模式,之后再讨论。

先总结一下旧有的2种模式:EAP、APM

从以下几个方面,看这2种异步编程方式的异同:
•命名、参数、返回值
•典型应用
•捕获异常
•状态
•取消操作
•进度报告

EAP
命名、参数、返回值
EAP的编程模式的代码命名有以下特点:
•将有一个或多个名为 “[方法名称]Async” 的方法。这些方法可能会创建同步版本的镜像,这些同步版本会在当前线程上执行相同的操作。
•该类还可能有一个 “[方法名称]Completed” 事件,监听异步方法的结果。
•它可能会有一个 “[方法名称]AsyncCancel”(或只是 CancelAsync)方法,用于取消正在进行的异步操作。
参数和返回值都没有特殊规定,按照业务需求而定

典型应用
以请求一个Url为例


代码如下:

public class EAP_Typical
{
public static void AsyncRun()
{
Utility.Log("AsyncRun:start");
//测试网址
string url = http://sports.163.com/nba/;
using (WebClient webClient = new WebClient())
{
//获取完成情况
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri(url));
Utility.Log("AsyncRun:download_start");
}
}
static void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string log = "AsyncRun:download_completed";
//获取返回结果
log += "|result_size=" + Utility.GetStrLen(e.Result);
Utility.Log(log);
}
}

捕获异常
异常信息一般在Completed的事件参数中传递的。紧接上面的例子,如果需要获取返回的异常信息,则需要改写一下DownloadStringComleted的方法。


代码如下:

static void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string log = "AsyncRun:download_completed";
if (e.Error != null) //可见,在事件的参数传输异常信息
{
//出现异常,就记录异常
log += "|error=" + e.Error.Message;
}
else
{
//没有出现异常,则记录结果
log += "|result_size=" + Utility.GetStrLen(e.Result);
}
Utility.Log(log);
}

状态
EAP本身并没有维护状态,如果需要的话,应该设置不同的时间响应不同的状态改变;
假设刚才的DownloadStringAsync,需要增加多几个状态值,可以考虑增加多几个事件。

Event DownloadStringStarted(响应下载刚开始)
Event DownloadStringPending(响应下载阻塞中)
Event DownloadStringCancel(响应下载取消时)
等等。

取消操作
按命名规范,如果操作对应有“[方法名称]AsyncCancel”(或只是 CancelAsync)方法,则支持取消操作。
取消的状态捕获,还是以刚才的下载Url输出html为例,还是在DownloadStringCompleted 获取取消与否的状态。DownloadStringCompletedEventArgs. Cancelled
注意的是,如果用户执行了CancelAsync后,在DownloadStringCompletedEventArgs.Error就会获取到对应的异常,此时不要再取DownloadStringCompletedEventArgs.Result。

进度报告
EAP没有硬性规定说要支持进度报告,但可以很顺其自然地通过时间响应进度变化。
以当前例子,WebClient 就提供了DownloadProgressChanged 做进度变化的响应事件。

APM
命名、参数、返回值
APM的编程模式的代码命名有以下特点:
•使用 IAsyncResult 设计模式的异步操作是通过名为[Begin操作名称] 和 [End操作名称] 的两个方法来实现的,这两个方法分别开始和结束异步操作 操作名称。 例如,FileStream 类提供 BeginRead 和 EndRead 方法来从文件异步读取字节。 这两个方法实现了 Read 方法的异步版本。
•在调用 [Begin操作名称] 后,应用程序可以继续在调用线程上执行指令,同时异步操作在另一个线程上执行。 每次调用 [Begin操作名称] 时,应用程序还应调用 [End操作名称] 来获取操作的结果。

典型应用
以请求一个Url为例


代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
namespace AsyncTest1.APM
{
public class APMTestRun1
{
public static void AsyncRun()
{
Utility.Log("APMAsyncRun:start");
//测试网址
string url = "http://sports.163.com/nba/";
HttpWebRequest webRequest = HttpWebRequest.Create(url) as HttpWebRequest;
webRequest.BeginGetResponse(Callback, webRequest);
Utility.Log("AsyncRun:download_start");
}
private static void Callback(IAsyncResult ar)
{
var source = ar.AsyncState as HttpWebRequest;
var response = source.EndGetResponse(ar);
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
Utility.Log("AsyncRun:result_size=" + Utility.GetStrLen(content));
}
}
}
}
}

委托的异步调用也用的是APM模式,这个方式的强大之处,在于可以使任何方法编程异步调用。


代码如下:

/// <summary>
/// 一个耗时的方法
/// </summary>
private static void CaluateManyNumber() {
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
Console.WriteLine("loop==>"+i.ToString());
}
}
/// <summary>
/// 委托,让耗时方法可以异步执行
/// </summary>
public static void AsyncDelegate() {
//委托简单的包装了一下方法
Action action = CaluateManyNumber;
action.BeginInvoke(DelegateCallback, null);
Console.WriteLine("action begin");
}
/// <summary>
/// 异步回调
/// </summary>
/// <param name="ar"></param>
private static void DelegateCallback(IAsyncResult ar) {
AsyncResult asyncResult = ar as AsyncResult;
var delegateSource = asyncResult.AsyncDelegate as Action;
delegateSource.EndInvoke(ar);
Console.WriteLine("action end");
}

捕获异常
异常信息要在[End操作名称]中获取。


代码如下:

private static void Callback(IAsyncResult ar)
{
var source = ar.AsyncState as HttpWebRequest;
WebResponse response = null;
try
{
response = source.EndGetResponse(ar);
}
catch (Exception ex) {
Utility.Log("error:" + ex.Message);
response = null;
}
if (response != null)
{
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string content = reader.ReadToEnd();
Utility.Log("AsyncRun:result_size=" + Utility.GetStrLen(content));
}
}
}
}

状态和取消操作、进度报告
APM模式本身不支持状态多样化和取消操作、进度报告。

(0)

相关推荐

  • .net4.5使用async和await异步编程实例

    关于异步编程的简单理解: 在.NET4.5中新增了异步编程的新特性async和await,使得异步编程更为简单.通过特性可以将这项复杂的工作交给编译器来完成了.之前传统的方式来实现异步编程较为复杂,这样对于程序猿来说处理起来比较困难,调试也没那么方便,后续的维护工作也比较痛苦. Async和Await关键字是C#异步编程的核心.通过使用这两个关键字,你可以使用.NET Framework 或 Windows Runtime的资源创建一个异步方法如同创建一个同步方法一样容易. 接下来通过VS201

  • 高效的.Net UDP异步编程实现分析

    因为要写一个网络程序要用到UDP协议,UDP这东西比较麻烦,又不像TCP一样提供可靠的连接,发送接收的超时实在不好设计,最后只要用Timer来检测有没有想要的数据包-_#,不过这不是这次的重点,重点是怎么建立一种高效的UDP机制来实时接收服务器发送过来的数据包. CodeProject上有个例子是开个线程去同步接收,这样倒是可以满足我的程序需求,不过实际中遇到几个问题: 1.程序开销大,内存狂飙,接一次数据就要重新开一次线程 2.由于主界面和底层是完全隔离只是通过中间的接口来通讯,导致线程总是不

  • 深入分析C#异步编程详解

    同步方法和异步方法的区别同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作异步编程概览.NET Framework 允许您异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法.BeginInvoke 方法用于启动异步调用.它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)

  • .NET中的异步编程-EAP/APM使用方法及案例介绍

    从.NET 4.5开始,支持的三种异步编程模式: •基于事件的异步编程设计模式 (EAP,Event-based Asynchronous Pattern) •异步编程模型(APM,Asynchronous Programming Model) •基于任务的编程模型(TAP,Task-based Asynchronous Pattern) 基于任务的异步模式 (TAP) 是基于 System.Threading.Tasks 命名空间的 Task 和 Task<TResult>,用于表示任意异步

  • .NET1.0版本中的异步编程模型(APM)

    一.概念 .NET 1.0提出了APM(Asynchronous Programming Model)即异步编程模式. .NET的类库有以BeginXXX和EndXXX类似的方法,就是使用异步编程模型. NET Framework很多类也实现了该模式,同时我们也可以自定义类来实现该模式,即在自定义的类中实现返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法,另外委托类型也定义了BeginInvoke和EndInvoke方法. 异步编程模型的本质 利用委托和线程池帮助我

  • nodejs中的异步编程知识点详解

    简介 因为javascript默认情况下是单线程的,这意味着代码不能创建新的线程来并行执行.但是对于最开始在浏览器中运行的javascript来说,单线程的同步执行环境显然无法满足页面点击,鼠标移动这些响应用户的功能.于是浏览器实现了一组API,可以让javascript以回调的方式来异步响应页面的请求事件. 更进一步,nodejs引入了非阻塞的 I/O ,从而将异步的概念扩展到了文件访问.网络调用等. 今天,我们将会深入的探讨一下各种异步编程的优缺点和发展趋势. 同步异步和阻塞非阻塞 在讨论n

  • C#5.0中的异步编程关键字async和await

    一.Asynchronous methods 异步方法 .NET 4.5 的推出,对于C#又有了新特性的增加——就是C#5.0中async和await两个关键字,这两个关键字简化了异步编程. 使用async修饰的方法被称为异步方法,这个方法调用时应该在前面加上await. 异步方法命名应该以Async结尾,这样大家知道调用的时候使用await. async和await关键字只是编译器的功能,编译器最终会用Task类创建代码. 1.创建返回任务的异步方法 建立一个同步方法Greeting,该方法在

  • 一文秒懂nodejs中的异步编程

    文章目录 简介同步异步和阻塞非阻塞javascript中的回调回调函数的错误处理回调地狱 ES6中的Promise什么是PromisePromise的特点Promise的优点Promise的缺点Promise的用法Promise的执行顺序 async和awaitasync的执行顺序async的特点 总结 简介 因为javascript默认情况下是单线程的,这意味着代码不能创建新的线程来并行执行.但是对于最开始在浏览器中运行的javascript来说,单线程的同步执行环境显然无法满足页面点击,鼠标

  • python中asyncio异步编程学习

    1.   想学asyncio,得先了解协程 携程的意义: 计算型的操作,利用协程来回切换执行,没有任何意义,来回切换并保存状态 反倒会降低性能. IO型的操作,利用协程在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能,从而实现异步编程(不等待任务结束就可以去执行其他代码 2.协程和多线程之间的共同点和区别: 共同点: 都是并发操作,多线程同一时间点只能有一个线程在执行,协程同一时间点只能有一个任务在执行: 不同点: 多线程,是在I/O阻塞时通过切换线

  • JavaScript中实现异步编程模式的4种方法

    你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推. 这种模式的好处是实现起来比较简单,执行环境相对单纯:坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行.常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他

  • Javascript异步编程的4种方法让你写出更出色的程序

    你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推.  这种模式的好处是实现起来比较简单,执行环境相对单纯:坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行.常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其

  • javascript异步编程的4种方法

    你可能知道,Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推.这种模式的好处是实现起来比较简单,执行环境相对单纯:坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行.常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务

  • Java之线程编程的4种方法实现案例讲解

    1.继承Thread public class T4 { public static void main(String[] args) { System.out.println(Thread.currentThread()); Thread t1 = new A1(); t1.start(); } } class A1 extends Thread{ @Override public void run() { for(int i=0;i<10;i++) { System.out.println(

随机推荐