深入理解C#中的异步(一)——APM模式EAP模式

1 使用异步编程的原因

同步编程,服务器在响A服务的数据库读取,网页请求或者文件请求(这里我们统称为IO操作),如果延迟很大,此时如果来了B服务的IO请求,可能无法及时响应(阻塞),此时异步编程模式(非阻塞)应运而生。

异步编程模式是为了避免性能瓶颈并增强你的应用程序的总体响应能力。

2 异步编程模式

2.1 APM模式

APM(Asynchronous Programming Model) 是 net 1.0时期就提出的一种异步模式,并且基于IAsyncResult接口实现BeginXXX和EndXXX类似的方法.

2.1.1 APM模式示例代码

    class Program
{
static void Main(string[] args)
{
Console.WriteLine("===== 异步调用 AsyncInvokeTest =====");
WebResponseHandler handler = new WebResponseHandler(WebContentLength.GetResult);
//IAsyncResult: 异步操作接口(interface)
//BeginInvoke: 委托(delegate)的一个异步方法的开始
IAsyncResult result = handler.BeginInvoke( null, null);
Console.WriteLine("继续做别的事情。");
//异步操作返回
Console.WriteLine(handler.EndInvoke(result));
Console.ReadKey();
}
}
public delegate string WebResponseHandler();
public class WebContentLength
{
public static string GetResult()
{
var client = new WebClient();
var content = client.DownloadString(new Uri("http://cnblogs.com"));
return "网页字数统计:"+content.Length;
}
}

2.1.2 执行结果

备注:APM又是建立在委托之上的。Net Core中的委托 不支持异步调用,也就是 BeginInvoke 和 EndInvoke 方法,即现代异步编程模型中,官方不推荐此模型。此例子使用 .Net FrameWork4.7框架。

2.1.3 APM回调例子

当异步请求响应完成之后,会自动去调用回调方法,将网页字数统计结果打印。

    class Program
{
static void Main(string[] args)
{
Console.WriteLine("===== 异步回调 AsyncInvokeTest =====");
WebResponseHandler handler = new WebResponseHandler(WebContentLength.GetResult);
//异步操作接口(注意BeginInvoke方法的不同!)
IAsyncResult result = handler.BeginInvoke( new AsyncCallback(CalllBack), "AsycState:OK");
Console.WriteLine("继续做别的事情。");
Console.ReadKey();
}
static void CalllBack(IAsyncResult result)
{
WebResponseHandler handler = (WebResponseHandler)((AsyncResult)result).AsyncDelegate;
Console.WriteLine(handler.EndInvoke(result));
Console.WriteLine(result.AsyncState);
}
}
public delegate string WebResponseHandler();
public class WebContentLength
{
public static string GetResult()
{
var client = new WebClient();
var content = client.DownloadString(new Uri("http://cnblogs.com"));
return "网页字数统计:" + content.Length;
}
}

备注:可以看出此种回调方式与人的思维逻辑相违背,当在回调函数中存在二级三级回调时,代码可读性变差,编程会变得比平常要困难一些。

2.1.4 执行结果

2.2 EAP模式

EAP(Event-based Asynchronous Pattern)基于事件的异步模式是 .net 2.0提出的,EAP异步编程算是C#对APM的一种补充,让异步编程拥有了一系列状态事件。实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步方法的取消、进度报告和报告结果。然而.net中并不是所有的类都支持EAP。

当我们使用EAP模式进行异步编程时,需要满足以下2个条件:

  1. 要进行异步的方法其方法名应该以XXXAsync结尾
  2. 要有一个名为XXXCompleted的事件监听异步方法的完成
  3. 可增加一个CancelAsync方法用于取消正在执行的异步方法(可选)

备注:当调用基于事件的EAP模式的类的XXXAsync方法时,就开始了一个异步操作,并且基于事件的EAP模式是基于APM模式之上的。EAP 是在 .NET Framework 2.0 版中引入的,在 winform,silverlight或者wpf变成中经常用到。

2.2.1 EAP模式编程示例1

    class Program
{
static void Main(string[] args)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += Wc_DownloadStringCompleted;
wc.DownloadStringAsync(new Uri("http://www.baidu.com"));
Console.WriteLine("执行其他任务。");
Console.ReadKey();
}
private static void Wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
Console.WriteLine("网页字数统计:" + e.Result.Length);
}
}

2.2.2 执行结果

总结:此示例代码的编程模式有没有种似曾相识的感觉。没错,winform,wpf等的点击事件,网络库的接收方法中采用事件驱动型的异步编程模式。

2.2.3 封装一个EAP例子

示例代码如下:

Work类,如下代码使用了了事件驱动型异步编程模式,并且对APM模式进行了封装。

    /// <summary>
/// EAP是对APM的封装
/// </summary>
public class Worker
{
public enum WorkerStatus
{
Cancel = 0, Running = 1, Completed = 2
}
public class WorkerEventArgs : EventArgs
{
public WorkerStatus Status { get; set; }
public string Message { get; set; }
}
public Worker()
{
}
public event EventHandler<WorkerEventArgs> OnWorkCompleted;
IAsyncResult asyncResult = null;
Thread thread = null;
public void WorkAsync()
{
Worker _this = this; Action action = () =>
{
thread = Thread.CurrentThread;
Thread.Sleep(1000);
Console.WriteLine(string.Format("线程:{0},Work Over.", Thread.CurrentThread.ManagedThreadId)); };
//result是IAsyncResult对象,此处无用
//当action委托完成调用之后,会调用如下回调方法。
asyncResult = action.BeginInvoke((result) =>
{
WorkerEventArgs e = null;
try
{
action.EndInvoke(result);
}
catch (ThreadAbortException ex)
{
e = new WorkerEventArgs() { Status = WorkerStatus.Cancel, Message = "异步操作被取消" };
}
if (null != _this.OnWorkCompleted)
{
_this.OnWorkCompleted.Invoke(this, e);
}
},this);
}
public void CancelAsync()
{
if (null != thread)
thread.Abort();
}
}

winform调用例子

异步嗲用WorkAsync,完成之后,事件异步调用WorkOver方法,并传入EventArgs参数。

    public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Worker worker;
private void btnStart_Click(object sender, EventArgs e)
{
worker = new Worker();
worker.OnWorkCompleted += WorkOver;
worker.WorkAsync();
Console.WriteLine(string.Format("线程:{0}", Thread.CurrentThread.ManagedThreadId));
} private void btnCancel_Click(object sender, EventArgs e)
{
worker.CancelAsync();
}
private void WorkOver(object sender, Worker.WorkerEventArgs e)
{
if (null != e)
{
if (Worker.WorkerStatus.Cancel == e.Status)
{
MessageBox.Show(e.Message);
}
}
else
{
Console.WriteLine(string.Format("线程:{0},委托回调完成.", Thread.CurrentThread.ManagedThreadId));
}
}
}

2.2.4 执行结果

  • 执行完成

  • 未执行完成提前取消

注意事项(重要):

  1. APM异步编程时,因异步代码执行在单独的线程中,异步代码中出现的异常应该在调用EndXXX时捕获。
  2. EAP异步编程时,因上述同样原因,代码中的异常信息会被传递到Completed事件的EventArgs参数中。

3 代码仓库

本文中的代码

4 下篇

预告:

深入理解C#中的异步(二)——TAP模式(基于Async,Await,Task的异步)


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.cnblogs.com/JerryMouseLi/p/14100496.html

深入理解C#中的异步(一)——APM模式EAP模式的更多相关文章

  1. 深入理解nodejs中的异步编程

    目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...

  2. 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)

    首先明确两点: 1.JS 执行机制是单线程. 2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行. 单线程执行带来什么问题? 在JS执行中都是单 ...

  3. .Net中的异步编程总结

    一直以来很想梳理下我在开发过程中使用异步编程的心得和体会,但是由于我是APM异步编程模式的死忠,当TAP模式和TPL模式出现的时候我并未真正的去接纳这两种模式,所以导致我一直没有花太多心思去整理这两部 ...

  4. .NET中的异步

    .NET中4种异步方式? ThreadPool.QueueUserworkItem实现 APM模式(就是BeginXXX和EndXXX成对出现.) EAP模式(就是Event based, 准确说来就 ...

  5. 看stackoverflow大牛如何回答何时在ASP.NET中使用异步控制器?

    转载自博客园:http://farb.cnblogs.com/ 今天无意中看到stackoverflow上一个很好的问答,个人觉得很有价值,所以翻译过来和大家共享!希望大家能相互交流. 在ASP.NE ...

  6. 全面理解Javascript中Promise

    全面理解Javascript中Promise 最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为 比较好的文章,供大家更好地学习js中非 ...

  7. javascript中的异步 macrotask 和 microtask 简介

    javascript中的异步 macrotask 和 microtask 简介 什么是macrotask?什么是microtask?在理解什么是macrotask?什么是microtask之前,我们先 ...

  8. 【转】简单理解Vue中的nextTick

    前言: Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick ...

  9. 第九节:深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)

    一. 并行编程 1. 区分串行编程和串行编程 ①. 串行编程:所谓的串行编程就是单线程的作用下,按顺序执行.(典型代表for循环 下面例子从1-100按顺序执行) ②. 并行编程:充分利用多核cpu的 ...

随机推荐

  1. 获取元素计算样式getComputedStyle()与currentStyle

    window.getComputedStyle()方法是标准化接口,返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值. 私有的CSS属性值可以通过对 ...

  2. fcntl函数用法——设置文件锁

    fcntl函数.锁定文件,设置文件锁.设置获取文件锁:F_GETLK .F_SETLK  .F_SETLKW文件锁结构,设置好用于fcntl函数的第三个参数.struct flock{    shor ...

  3. 解决calamari无法获取节点信息的bug

    前言 一直在做calamari的相关的一些打包和安装的工作,都是业余弄的东西,所以并没有仔细的进行功能点的验证测试,正好ceph社区群里面有人问了个问题 calamari上是不是能看到ceph的ver ...

  4. yum安装Ceph指定Jewel版本

    前言 通过yum安装指定的rpm包,这个一般是 yum --showduplicates list ceph | expand ,然后去通过yum安装指定的版本即可,这个在hammer下是没有问题的, ...

  5. 【芯片手册开发】Sil9136音频开发详细分析+源码实战

    目录 前言 参考 手册使用+实战 配置 Configuring Audio Using I2S 总结实现 前言 默认在开发了视频方面后 这方面的工作本来可以找技术支持拿个例程参考下,很快就可以的写出来 ...

  6. freshclam

    1 3 * ** /usr/local/clamav/bin/freshclam 定时更新.定时杀毒

  7. CorelDRAW中的合并和群组功能

    在设计过程中,对象就是我们进行操作的主体,这个操作主体可以是单个对象,也可以是多个对象,在有多个对象要进行处理时,就需要用到类似CorelDRAW里的合并和群组这两个功能了. 一.合并与拆分 合并功能 ...

  8. Nacos安装与启动教程

    前言 Nacos是阿里巴巴集团开源的一个易于使用的平台,专为动态服务发现,配置和服务管理而设计,Nacos基本上支持现在所有类型的服务,例如,Dubbo / gRPC服务,Spring Cloud R ...

  9. So Easy! HDU - 4565

    易知,有\(S_n = \lceil{a + \sqrt{b}}\rceil ^ n\) \(\because a ^ 2 - 1 < b < a ^ 2\) \(\therefore a ...

  10. linux命令-awk,sort,uniq

    学习地址:http://man.linuxde.net/awk#awk的工作原理 awk 选项参数说明: -F fs or --field-separator fs 指定输入文件折分隔符,fs是一个字 ...