什么是异步编程模型

异步编程模型(Asynchronous Programming Model,简称APM)是C#1.1支持的一种实现异步操作的编程模型,虽然已经比较“古老”了,但是依然可以学习一下的。通过对APM的学习,我总结了以下三点:

1. APM的本质是使用委托和线程池来实现异步编程的。

2. 实现APM的关键是要实现IAsyncResult接口

3. 实现了APM的类都会定义一对形如BeginXXX()和EndXXX()的方法,例如,FileStream类定义了BeginRead()方法和EndRead()方法,可以实现异步读取文件内容。

下面我们就通过具体的代码来实现异步编程模型。

实现异步编程模型

1. 实现IAsyncResult接口

IAsyncResult接口是C#类库中定义的一个接口,表示异步操作的状态,具体介绍可以查看MSDN

  1. public interface IAsyncResult
  2. {
  3. object AsyncState { get; }
  4.  
  5. WaitHandle AsyncWaitHandle { get; }
  6.  
  7. bool CompletedSynchronously { get; }
  8.  
  9. bool IsCompleted { get; }
  10. }

上面的代码是IAsyncResult接口声明的四个属性:

1. AsyncState属性是一个用户定义的对象,包含异步操作状态信息。例如,当我们调用FileStream类的BeginRead()方法进行异步读取文件内容时,传入的最后一个参数对应的就是AsyncState属性。

2. AsyncWaitHandle属性主要的作用是阻塞当前线程来等待异步操作完成。WaitHandle抽象类,有一个很重要的派生类ManualResetEvent。

3. CompletedSynchronously属性比较特别,用来判断异步操作是否是同步完成(这个有点儿绕~)。

4. IsCompleted属性就比较简单了,用来判断异步操作是否完成,true表示已完成,false表示还未完成。

在实现IAsyncResult接口时,我们主要会用到AsyncState,IsCompleted和AsyncWaitHandle属性。

  1. /// <summary>
  2. /// CalculatorAsyncResult<T>类,实现了IAsyncResult接口
  3. /// </summary>
  4. /// <typeparam name="T"></typeparam>
  5. public class CalculatorAsyncResult<T> : IAsyncResult
  6. {
  7. private ManualResetEvent _waitHandle;
  8.  
  9. private object _asyncState;
  10.  
  11. private bool _completedSynchronously;
  12.  
  13. private bool _isCompleted;
  14.  
  15. //我们传入的异步回调方法
  16. private AsyncCallback _asyncCallback;
  17.  
  18. //保存异步操作返回结果
  19. public T CalulatorResult { get; set; }
  20.  
  21. public static CalculatorAsyncResult<T> CreateCalculatorAsyncResult(Func<T> work, AsyncCallback asyncCallback, object obj)
  22. {
  23. var asyncResult = new CalculatorAsyncResult<T>(obj, asyncCallback, false, false);
  24.  
  25. asyncResult.ExecuteWork(work);
  26.  
  27. return asyncResult;
  28. }
  29.  
  30. public CalculatorAsyncResult(object obj, AsyncCallback asyncCallback, bool completedSynchronously, bool isCompleted)
  31. {
  32. _waitHandle = new ManualResetEvent(false);
  33.  
  34. _asyncState = obj;
  35.  
  36. _completedSynchronously = completedSynchronously;
  37.  
  38. _isCompleted = isCompleted;
  39.  
  40. _asyncCallback = asyncCallback;
  41. }
  42.  
  43. public object AsyncState
  44. {
  45. get { return _asyncState; }
  46. }
  47.  
  48. public WaitHandle AsyncWaitHandle
  49. {
  50. get{ return _waitHandle; }
  51. }
  52.  
  53. public bool CompletedSynchronously
  54. {
  55. get { return _completedSynchronously; }
  56. }
  57.  
  58. public bool IsCompleted
  59. {
  60. get { return _isCompleted; }
  61. }
  62.  
  63. public void Wait()
  64. {
  65. _waitHandle.WaitOne();
  66. }
  67.  
  68. /// <summary>
  69. /// 调用异步回调方法
  70. /// </summary>
  71. private void InvokeAsyncCallback()
  72. {
  73. _isCompleted = true;
  74.  
  75. if (_waitHandle != null)
  76. {
  77. _waitHandle.Set();
  78. }
  79.  
  80. //调用我们传入的异步回调方法
  81. _asyncCallback(this);
  82. }
  83.  
  84. /// <summary>
  85. /// 执行异步工作
  86. /// </summary>
  87. /// <param name="work"></param>
  88. public void ExecuteWork(Func<T> work)
  89. {
  90. if(_asyncCallback != null)
  91. {
  92. Task<T> task = Task.Factory.StartNew<T>(work);
  93.  
  94. task.ContinueWith(t =>
  95. {
  96. CalulatorResult = t.Result;
  97.  
  98. InvokeAsyncCallback();
  99. });
  100. }
  101. else
  102. {
  103. _isCompleted = true;
  104.  
  105. if(_waitHandle != null)
  106. {
  107. _waitHandle.Set();
  108. }
  109. }
  110. }
  111. }

2. 定义BeginXXX()和EndXXX()方法

下面就来定义我们自己的APM接口和具体实现类,编写BeginXXX()和EndXXX()方法。

  1. /// <summary>
  2. /// 异步计算接口
  3. /// </summary>
  4. /// <typeparam name="T"></typeparam>
  5. public interface ICalculator<T>
  6. {
  7. IAsyncResult BeginAdd(T x, T y, AsyncCallback asyncCallback, Object obj);
  8.  
  9. T EndAdd(IAsyncResult ar);
  10. }
  1. /// <summary>
  2. /// 异步计算接口实现类
  3. /// </summary>
  4. public class Calculator : ICalculator<double>
  5. {
  6. public IAsyncResult BeginAdd(double x, double y, AsyncCallback asyncCallback, Object obj)
  7. {
  8. return CalculatorAsyncResult<double>.CreateCalculatorAsyncResult(delegate { return Add(x, y); }, asyncCallback, obj);
  9. }
  10.  
  11. public double EndAdd(IAsyncResult ar)
  12. {
  13. var calculatorAsyncResult = (CalculatorAsyncResult<double>)(ar);
  14.  
  15. calculatorAsyncResult.Wait();
  16.  
  17. return calculatorAsyncResult.CalulatorResult;
  18. }
  19.  
  20. /// <summary>
  21. /// 计算方法
  22. /// </summary>
  23. /// <param name="x"></param>
  24. /// <param name="y"></param>
  25. /// <returns></returns>
  26. protected double Add(double x, double y)
  27. {
  28. Console.WriteLine("Async thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId);
  29.  
  30. Console.WriteLine("Async thread(id={0}) is calculating...\n", Thread.CurrentThread.ManagedThreadId);
  31.  
  32. Thread.Sleep();
  33.  
  34. var r = x + y;
  35.  
  36. Console.WriteLine("Async thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
  37.  
  38. return r;
  39. }
  40. }

3. 获取异步操作结果

APM提供了四种获取异步操作的结果方式供我们选择:

1. 通过IAsyncResult的AsyncWaitHandle属性,调用它的WaitOne()方法使调用线程阻塞来等待异步操作完成再调用EndXXX()方法来获取异步操作结果。

2. 在调用BeginXXX()方法的线程上调用EndXXX()方法来获取异步操作结果。这种方式也会阻塞调用线程(阻塞原理同方式1,具体在上面的代码中有体现)。

3. 轮询IAsyncResult的IsComplete属性,当异步操作完成后再调用EndXXX()方法来获取异步操作结果。

4. 使用 AsyncCallback委托来指定异步操作完成时要回调的方法,在回调方法中调用EndXXX()方法来获取异步操作结果。

在上述的四种方式中,只有第四种方式是完全不会阻塞调用线程的,所以多数情况下我们都会选择回调的方式来获取异步操作结果。

  1. public class Program
  2. {
  3. public static double result = ;
  4.  
  5. static void Main(string[] args)
  6. {
  7. Console.WriteLine("Main thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId);
  8.  
  9. var calculator = new Calculator();
  10.  
  11. Console.WriteLine("Main thread(id={0}) invokes BeginAdd() function.\n", Thread.CurrentThread.ManagedThreadId);
  12.  
  13. calculator.BeginAdd(, , Callback, calculator);
  14.  
  15. Console.WriteLine("Main thread(id={0}) is sleeping...\n", Thread.CurrentThread.ManagedThreadId);
  16.  
  17. Thread.Sleep();
  18.  
  19. Console.WriteLine("The calculating result of async operation is {0}.\n", result);
  20.  
  21. Console.WriteLine("Main thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
  22. }
  23.  
  24. /// <summary>
  25. /// 我们定义的回调方法
  26. /// </summary>
  27. /// <param name="ar"></param>
  28. public static void Callback(IAsyncResult ar)
  29. {
  30. var calculator = (Calculator)(ar.AsyncState);
  31.  
  32. result = calculator.EndAdd(ar);
  33. }
  34. }

运行结果:

至此,我们已经完整地实现了APM异步编程模型,从运行结果中我们可以得出,通过回调的方式来获取异步操作结果是完全不会阻塞调用线程的。

总结

1. 实现APM的关键是实现IAsyncResult接口。在IAsyncResult实现类中,需要使用线程池来异步地执行操作,在操作完成之后,再调用传入的回调方法来返回操作结果。

2. 实现了APM的类中都会定义一对BeginXXX()和EndXXX()方法,开始异步操作,结束异步操作并返回异步操作结果。

3. 获取异步操作结果有四种方式,但是只有回调方式是完全不会阻塞调用线程的,其他的都会阻塞调用线程。

C#异步编程模型的更多相关文章

  1. JS魔法堂:深究JS异步编程模型

    前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...

  2. 多线程之异步编程: 经典和最新的异步编程模型,async与await

    经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...

  3. 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换

    经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经 ...

  4. 谈谈c#中异步编程模型的变迁

    大家在编程过程中都会用到一些异步编程的情况.在c#的BCL中,很多api都提供了异步方法,初学者可能对各种不同异步方法的使用感到迷惑,本文主要为大家梳理一下异步方法的变迁以及如何使用异步方法. Beg ...

  5. 深究JS异步编程模型

    前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...

  6. 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换

    [源码下载] 重新想象 Windows 8 Store Apps (44) - 多线程之异步编程: 经典和最新的异步编程模型, IAsyncInfo 与 Task 相互转换 作者:webabcd 介绍 ...

  7. 【温故知新】c#异步编程模型(APM)--使用委托进行异步编程

    当我们用到C#类许多耗时的函数XXX时,总会存在同名的类似BeginXXX,EndXXX这样的函数. 例如Stream抽象类的Read函数就有 public abstract int Read(byt ...

  8. 《C#并行编程高级教程》第9章 异步编程模型 笔记

    这个章节我个人感觉意义不大,使用现有的APM(异步编程模型)和EAP(基于时间的异步模型)就很够用了,针对WPF和WinForm其实还有一些专门用于UI更新的类. 但是出于完整性,还是将一下怎么使用. ...

  9. C#异步编程の-------异步编程模型(APM)

    术语解释: APM               异步编程模型, Asynchronous Programming Model EAP                基于事件的异步编程模式, Event ...

  10. .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)

    本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...

随机推荐

  1. xcode9 上传app后iTues 构建版本不显示

    1.问题原因 苹果公司更新了ios10系统和xcode9以后,做了许多调整,如果开发者没有注意就会遇到这样那样的问题.作者在更新以后就遇到了上传app到appstore成功后,没有显示的问题.下面就介 ...

  2. day6学python 生成器迭代器+压缩文件

    生成器迭代器+压缩文件 readme的规范 1软件定位,软件的基本功能2运行代码的方法:安装环境,启动命令3简要的使用说明4代码目录结构说明,更详细点可以说明软件的基本原理5常见问题说明 ====== ...

  3. 解决JAR包里面打开源代码都是乱码

    下面是解决方案 通过eclipse浏览源代码时,发现中文注释为乱码的问题.其实这个eclipse默认编码造成的问题.可以通过以下方法解决: 修改Eclipse中文本文件的默认编码:windows-&g ...

  4. 解决双击dwg文件ARX自定义实体提示代理的问题

    双击dwg文件的时候,如果没有通过注册表设置会提示代理实体. 注册表自动加载arx 注册表参考路径 R18.1 是cad版本 ACAD-9001:409 是cad的地区语言,409是英文 ,804是中 ...

  5. C# winform间窗体传值简单Demo

    form1是用来接收值的 using System; using System.Collections.Generic; using System.ComponentModel; using Syst ...

  6. Deploy Flask app to Apache on Windows

    内容已过期,分割线以下为原文存档. 故事背景 这次我需要将一个Flask应用部署到本地的Windows服务器上.操作系统是64位的,程序是基于Python 3开发的,大体就是这样. 部署选项 根据Fl ...

  7. Linux硬件信息采集

    dmidecode: 简介: dmidecode命令通过读取DMI数据库获取硬件信息并输出.由于DMI信息可以人为修改,因此里面的信息不一定是系统准确的信息 dmidecode遵循SMBIOS/DMI ...

  8. tornado 10 长轮询和 websocket

    tornado 10 长轮询和 websocket 一.长轮询 #在网页,我们经常扫码登录,那么问题来了,前端是如何知道用户在手机上扫码登录的呢 这里就需要用到长轮询 #长轮询 #客户端能够不断地向服 ...

  9. php-fpm 三种运行模式

    php-fpm配置 配置文件:php-fpm.conf 开启慢日志功能的: slowlog = /usr/local/var/log/php-fpm.log.slowrequest_slowlog_t ...

  10. 洛谷 P3182 [HAOI2016]放棋子(错排问题)

    题面 luogu 题解 裸的错排问题 错排问题 百度百科:\(n\)个有序的元素应有\(n!\)个不同的排列,如若一个排列使得所有的元素不在原来的位置上,则称这个排列为错排:有的叫重排.如,1 2的错 ...