在构建高性能、可伸缩的应用程序时,必定会采用异步操作来提升应用程序性能,改善用户体验,异步操作与线程池结合允许使用很少的线程执行许多的操作。CLR中提供了一种异步操作的模式即异步编程模式。

1.异步编程模型简介

异步编程模式中的方法都是采用BeginXxx方法开始执行异步操作和EndXxx方法结束异步操作。BeginXxx方法都接受一个AsyncCallback委托类型的回调函数和回调函数需要使用的一个object类型的数据对象以及其他的一些操作参数,并且该方法会返回一个实现了IAsyncResult接口类型对象,EndXxx方法都接受一个IAsyncResult接口的类型参数。AsyncCallback委托类型定义如下:

public delegate void AsyncCallback(IAsyncResult ar);

IAsyncResult接口包含一个获取用户定义的数据以及包含异步操作的信息的属性AsyncState, 获取用于等待异步操作完成的 System.Threading.WaitHandle对象引用,获取一个指示异步操作是否同步完成的值CompletedSynchronously以及指示异步操作是否已完成的值IsCompleted。

采用异步编程时,调用了BeginXxx方法后CLR内部会维护一个BeginXxx方法返回的IAsyncResult类型的实例引用并在异步操作完成后调用用户的回调方法,同时向回调方法传递内部维护的IAsyncResult实例的引用。在回调方法中总是需要调用EndXxx方法一次并且只能调用一次,并传递BeginXxx方法调用时所维护的一个IAsyncResult类型的实例引用,调用BeginXxx方法和EndXxx方法的对象必须为相同对象,如果类型不相同就会抛出InvalidOperationException异常(所提供的IAsyncResult对象与此委托不相同。)。初始化异步操作时CLR会分配一些内部资源,操作完成以后CLR会保留这些资源直到调用EndXxx方法时,它会访问这些内部资源并释放它们,如果不调用EndXxx方法就无法获取到异步操作是否完成以及操作中是否抛出了异常,同时也会浪费资源。

在FCL中对许多的类型都提供了异步操作,尤其是与I/O操作相关的类都提供了异步编程模式,如下一些类型都提供了异步操作的功能:

  1. 所有派生自 System.IO.Stream 的类并与硬件通信的类型(如:FileStream,NetWorkStream等)都提供了BeginRead和BeginWrite方法来执行异步操作。
  2. 所有派生自 System.Net.WebRequest 的类(如:HttpWebRequest,FtpWebRequest等)都提供了BeginGetRequstStream和BeginGetResponse方法。
  3. System.Data.SqlClient.SqlCommand 类提供了BeginExecuteNonQuery、BeginExecuteReader和BeginExecuteXmlReader方法。
  4. System.Net.Sockets.Socket 类提供了BeginAccept、BeginConnect、BeginDisconnect、BeginReceive、BeginReceiveFrom、BeginReceiveMessageFrom、BeginSend、BeginSendFile和BeginSendTo方法。

在创建委托时编译器会自动为委托类型增加APM的方法BeginInvoke和EndInvoke方法。

2.异步的I/O操作

在应用程序中,一般都存在大量的自定义配置或数据文件,比如:用XML文件存储的用户数据或配置信息,用Json格式存储的数据文件等。在操作这些文件的时候为了不阻塞工作者线程提高相应速度都会采用异步的读写方式来读取或更新文件。如下示例表示异步的写入和读取Json格式数据:

   1:  private readonly static string filePath = "http://www.cnblogs.com/JsonData/JsonConfig.json";
   2:          private static void AsyncWriteAndReadData()
   3:          {
   4:              //异步写入文件
   5:              FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 1024, FileOptions.Asynchronous);
   6:              Dictionary<string, object> config = new Dictionary<string, object>() { 
   7:                  {"Name","APMJsonData"},
   8:                  {"Time",DateTime.Now.ToString()},
   9:                  {"Person","Stone"}
  10:                  };
  11:              System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
  12:              string json = serializer.Serialize(config);
  13:              byte[] bt = Encoding.UTF8.GetBytes(json);
  14:              fs.BeginWrite(bt, 0, bt.Length, state =>
  15:              {
  16:                  FileStream fsEnd = state.AsyncState as FileStream;
  17:                  if (fsEnd != null)
  18:                  {
  19:                      fsEnd.EndWrite(state);
  20:                      fsEnd.Dispose();
  21:                      Console.WriteLine("异步写入数据成功!");
  22:                  }
  23:   
  24:              }, fs);
  25:              //阻塞线程,文件资源释放以后再读取数据
  26:              Thread.Sleep(2000);
  27:              //异步的读取文件
  28:              fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 1024, FileOptions.Asynchronous);
  29:              fs.BeginRead(bt, 0, bt.Length, state =>
  30:              {
  31:                  FileStream fsEnd = state.AsyncState as FileStream;
  32:                  if (fsEnd != null)
  33:                  {
  34:                      fsEnd.EndRead(state);
  35:                      fsEnd.Dispose();
  36:                      json = Encoding.UTF8.GetString(bt);
  37:                      var configInfo = serializer.Deserialize<Dictionary<string, object>>(json.Replace("\0", ""));
  38:                      foreach (var item in configInfo)
  39:                      {
  40:                          Console.WriteLine("{0}:{1}", item.Key, item.Value);
  41:                      }
  42:                  }
  43:              }, fs);
  44:          }

写入后的文件内容:{"Name":"APMJsonData","Time":"2012/12/24 21:26:01","Person":"Stone"}

读取数据结果:

如上代码所示在异步写入文件或异步读取文件时使用的BeginXxx方法并在回调方法中调用了EndXxx方法。使用FileStream类异步操作文件时需要注意必须知道文件的操作方法:文件是否可用于异步读取和写入,即创建FileStream实例时需要采用带有FileOptions类型参数的重载构造函数,指定FileOptions.Asynchronous值时操作文件才会以异步的方方式读取和写入。也可以使用如下的构造函数来创建实例:

public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync);
 
 
public delegate void SendOrPostCallback(object state);
 
   1:   private void button1_Click(object sender, EventArgs e)
   2:          {
   3:              //获得当前线程的同步上下文
   4:              SynchronizationContext syncContext = SynchronizationContext.Current;
   5:              string url =this.textBox1.Text;
   6:              //创建WebRequest对象并异步请求资源
   7:              WebRequest request = HttpWebRequest.Create(url);
   8:              request.BeginGetResponse(state =>
   9:              {
  10:                  WebRequest webReq = state.AsyncState as WebRequest;
  11:                  using (WebResponse response = webReq.EndGetResponse(state))
  12:                  {
  13:                      using (var stream = response.GetResponseStream())
  14:                      {
  15:                          if (response.ContentLength != -1)
  16:                          {
  17:                              byte[] bt = new byte[response.ContentLength];
  18:                              stream.Read(bt, 0, bt.Length);
  19:                              string content = Encoding.Default.GetString(bt);
  20:                              //使用同步上下文的Post方法同步更新UI
  21:                              syncContext.Post(obj =>
  22:                              {
  23:                                  this.textBox2.Text = obj.ToString();
  24:                              }, content);
  25:                          }
  26:                      }
  27:                  }
  28:              }, request);
  29:          }

执行结果:

 
 
   1:   private static void AsyncMethod()
   2:          {
   3:              //定义一个与方法声明相同的委托来异步执行方法
   4:              Func<int> apm = () => {
   5:                  //耗时的计算任务
   6:                  Thread.Sleep(2000);
   7:                  int sum = 0;
   8:                  for (int i = 0; i < 100; i++)
   9:                  {
  10:                      sum += i;
  11:                  }
  12:                  return sum;
  13:              };
  14:   
  15:              apm.BeginInvoke(state => {
  16:                int sum=  apm.EndInvoke(state);
  17:                Console.WriteLine("使用委托异步执行方法,结果为:{0}",sum.ToString());
  18:              },null);
  19:          }

执行结果:

4.将APM转换为Task来执行异步操作

在TaskFactory类中提供了一个FromAsync方法,它包含4个参数,异步操作的开始方法BeginXxx、异步操作的结束方法EndXxx、Object类型的状态数据和TaskCreationOptions 类型的参数。定义如下:

 public Task FromAsync(Func<AsyncCallback, object, IAsyncResult> beginMethod, Action<IAsyncResult> endMethod, object state, TaskCreationOptions creationOptions);

该方法返回一个Task类型的实例,通过该方法可以把一个APM转换为任务来执行。

Task类实现了IAsyncResult接口,Task的AsyncState属性就是IAsyncResult接口的AsyncState属性,通过该属性就可以获得传给TaskFactory类FromAsync方法的数据对象(state的值)。

将异步I/O转换为Task来执行示例:

   1:  private void APMToTask() {
   2:              //获得当前线程的同步上下文
   3:              SynchronizationContext syncContext = SynchronizationContext.Current;
   4:              string url = this.textBox1.Text;
   5:              //创建WebRequest对象并异步请求资源
   6:              WebRequest request = HttpWebRequest.Create(url);
   7:              //通过Task类的Factory属性获得TaskFactory实例,并调用FromAsync方法将异步I/O操作转换为任务执行
   8:              var task= Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,request.EndGetResponse,request,TaskCreationOptions.None);
   9:              task.ContinueWith(t => {
  10:                  //任务完成以后启动延续任务,并获得异步请求数据。
  11:                  using (WebResponse response =t.Result)
  12:                  {
  13:                      using (var stream = response.GetResponseStream())
  14:                      {
  15:                          if (response.ContentLength != -1)
  16:                          {
  17:                              byte[] bt = new byte[response.ContentLength];
  18:                              stream.Read(bt, 0, bt.Length);
  19:                              string content = Encoding.Default.GetString(bt);
  20:                              //使用同步上下文的Post方法同步更新UI
  21:                              syncContext.Post(obj =>
  22:                              {
  23:                                  this.textBox2.Text = obj.ToString();
  24:                              }, content);
  25:                          }
  26:                      }
  27:                  }
  28:              });
  29:          }

执行结果:

本文简单的介绍了异步编程模型(APM)的知识,这种模式非常适合与I/O相关的操作,对于开发高性能、可伸缩的应用程序是非常有用的技术,比如异步获取网络资源,异步操作文件等。

转载于:https://www.cnblogs.com/pengstone/archive/2012/12/25/2833112.html

C# 线程知识--异步编程模型(APM)相关推荐

  1. [你必须知道的异步编程]——异步编程模型(APM)

    本专题概要: 引言 你知道APM吗? 你想知道如何使用异步编程模型编写代码吗? 使用委托也可以实现异步编程,你知道否? 小结 一.引言 在前面的C#基础知识系列中介绍了从C#1.0--C#4.0中一些 ...

  2. 【转】1.6异步编程:IAsyncResult异步编程模型 (APM)

    传送门:异步编程系列目录-- 大部分开发人员,在开发多线程应用程序时,都是使用ThreadPool的QueueUserWorkItem方法来发起一次简单的异步操作.然而,这个技术存在许多限制.最大的问 ...

  3. 《CLR Via C# 第3版》笔记之(二十一) - 异步编程模型(APM)

    APM是.NET中异步编程模型的缩写(Asynchronous Programing Model). 通过异步编程,使得我们的程序可以更加高效的利用系统资源. 主要内容: 一个APM的例子 GUI中的 ...

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

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

  5. 【转】异步编程:.NET 4.5 基于任务的异步编程模型(TAP)

    最近我为大家陆续介绍了"IAsyncResult异步编程模型 (APM)"和"基于事件的异步编程模式(EAP)"两种异步编程模型.在.NET4.0 中Micro ...

  6. 【转】1.8异步编程:.NET 4.5 基于任务的异步编程模型(TAP)

    传送门:异步编程系列目录-- 最近我为大家陆续介绍了"IAsyncResult异步编程模型 (APM)"和"基于事件的异步编程模式(EAP)"两种异步编程模型. ...

  7. C# 异步编程模式 APM、EAP、TPL

    目录 异步编程模式 APM异步编程模型 APM的本质 APM的实现 读取的同步方法: BeginXxx方法--读取的异步方法: EndXxx方法--结束异步操作 异步调用的结果--与IAsyncRes ...

  8. c# 三种异步编程模型EAP(*)、 APM(*)和 TPL

    为什么80%的码农都做不了架构师?>>>    EAP 是 Event-based Asynchronous Pattern(基于事件的异步模型)的简写 优点是简单,缺点是当实现复杂 ...

  9. 简单地使用线程之一:使用异步编程模型

    .NetFramework的异步编程模型从本质上来说是使用线程池来完成异步的任务,异步委托.HttpWebRequest等都使用了异步模型. 这里我们使用异步委托来说明异步编程模型. 首先,我们来明确 ...

最新文章

  1. java的父类java.lang.object_根父类:java.lang.Object
  2. attempt to create delete event with null entity
  3. iconv android 编译,将iconv编译成lua接口
  4. C# 读写Ini文件
  5. 最小生成树--Boruvka算法
  6. Html之图片轮播(锚)
  7. Go(4 [Map])
  8. time_t 和 struct tm 及时间戳的正确用法
  9. Python中非纯文本文件的读取
  10. Luogu5607 [Ynoi2013] 无力回天 NOI2017
  11. 2021荣耀秋招笔试代码题
  12. PDF转Word教程
  13. JQuery 如何使用插件如何安装插件(详细讲解)
  14. 应用数据安全,任重而道远
  15. snap 无法卸载_你手机里有哪些不想卸载的良心 App?
  16. c++语言程序中,要调用的函数必须在main()函数中定义,惠州学院C++考试复习题
  17. 自己写一个微型数据库_“最国际化的微型机构:”两名伦敦训练营的毕业生如何建造了一个远程…...
  18. 视频图像数据处理八:将rgb视频图像转换为yuv420格式视频图像
  19. YOLOv4网络详解
  20. TortoiseGit-下载安装汉语语言包(汉化-方法)

热门文章

  1. wpf button无边框_中国式新房无玄关?客厅真不缺这点面积!
  2. distinct性能问题_Mysql性能优化:如何给字符串加索引?
  3. Java1009_疯狂java学习笔记1009---异常
  4. MySQL存个人信息可以吗_mysql数据库中,在修改数据时能否保存操作人员的信息,比如姓名或id,如果能,怎么操作。...
  5. IDEA将maven项目复制成一个新的框架/项目
  6. 内存模型 linux,内存模型 - STM32F4 编程手册学习_Linux编程_Linux公社-Linux系统门户网站...
  7. python pytorch自定义_Pytorch 实现自定义参数层的例子
  8. java 删除指定文件夹和下面所有文件_JAVA语言基础
  9. Python 学习之旅1
  10. 大数据在电力行业的应用前景有哪些?