一、APM概述

APM即异步编程模型的简写(Asynchronous Programming Model),我们平时经常会遇到类似BeginXXX和EndXXX的方法,我们在使用这些方法的时候,其实就是在使用APM来编写程序。

本质:线程池+委托

线程池会在后台执行异步操作,执行完成后,通过回调函数来获取执行结果。

一般使用步骤:

1)构建一个对象,调用BeginXXX异步方法,方法的参数中一般会传入一个委托(回调函数)和一个Object类型变量(用于传递调用BeginXXX异步方法的对象或者封装了该对象的对象)

   回调函数的类型:返回值为void,参数为IAsyncResult asyncResult;

2)在回调函数中,利用IAsyncResult的AsyncState属性来获得传入的Object对象,从中获取调用BeginXXX异步方法的对象,接着调用EndXXX,根据EndXXX的返回值判断操作是否完成;

如果没有完成,继续调用BeginXXX异步方法,循环往复,直至操作完成;

二、Demo

以下演示了使用APM模式下载jpg图像。

  1 using System;
2 using System.Diagnostics;
3 using System.IO;
4 using System.Net;
5 using System.Windows;
6 using System.Threading;
7
8 namespace Wpf_APM_BeginEnd
9 {
10 // Asynchronous Programming Model
11 //APM .Net 1.0 不支持对异步操作的取消和没有提供对进度报告的功能
12 public class RequestState
13 {
14 private HttpWebRequest request;
15 public HttpWebRequest Request
16 {
17 get
18 {
19 return request;
20 }
21 set
22 {
23 request = value;
24 }
25 }
26 private HttpWebResponse response;
27 public HttpWebResponse Response
28 {
29 get
30 {
31 return response;
32 }
33 set
34 {
35 response = value;
36 }
37 }
38 public Stream ResponseStream;
39 public FileStream Filestream = null;
40
41 public byte[] BufferRead = new byte[1024];
42 public static int Index = 1;
43 public RequestState(string fileSavePath)
44 {
45 string fileName = "Pic" + (Index++).ToString();
46 string saveFilePath = fileSavePath + fileName + ".jpg";//以下载jpg图片为例
47 Filestream = new FileStream(saveFilePath, FileMode.CreateNew);
48 }
49 }
50 /// <summary>
51 /// Interaction logic for MainWindow.xaml
52 /// </summary>
53 public partial class MainWindow : Window
54 {
55 private string downLoadUrl = @"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2298824648,1812234339&fm=200&gp=0.jpg";
56 public string DownLoadUrl
57 {
58 get { return downLoadUrl; }
59 set { downLoadUrl = value; }
60 }
61 private string fileSavePath = @"D:\360Downloads\";
62 public string FileSavePath
63 {
64 get { return fileSavePath; }
65 set { fileSavePath = value; }
66 }
67 public MainWindow()
68 {
69 InitializeComponent();
70 this.DataContext = this;
71 }
72
73 #region use APM to download file asynchronously
74
75 private void DownloadFileAsync(string url)
76 {
77 try
78 {
79 Debug.WriteLine($"ThreadId: {Thread.CurrentThread.ManagedThreadId}");
80 // Initialize an HttpWebRequest object
81 HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
82 // Create an instance of the RequestState and assign HttpWebRequest instance to its request field.
83 RequestState requestState = new RequestState(FileSavePath);
84 requestState.Request = myHttpWebRequest;
85 myHttpWebRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), requestState);
86 }
87 catch (Exception e)
88 {
89 MessageBox.Show(e.Message);
90 }
91 }
92
93 // The following method is called when each asynchronous operation completes.
94 private static void ResponseCallback(IAsyncResult callbackresult)
95 {
96 // Get RequestState object
97 Debug.WriteLine($"ResSubThreadId: {Thread.CurrentThread.ManagedThreadId}");
98 RequestState myRequestState = (RequestState)callbackresult.AsyncState;
99
100 HttpWebRequest myHttpRequest = myRequestState.Request;
101
102 // End an Asynchronous request to the Internet resource
103 myRequestState.Response = (HttpWebResponse)myHttpRequest.EndGetResponse(callbackresult);
104
105 // Get Response Stream from Server
106 Stream responseStream = myRequestState.Response.GetResponseStream();
107 myRequestState.ResponseStream = responseStream;
108
109 IAsyncResult asynchronousRead = responseStream.BeginRead(myRequestState.BufferRead, 0, myRequestState.BufferRead.Length, ReadCallBack, myRequestState);
110
111
112 App.Current.Dispatcher.BeginInvoke(new Action(()=> { }));
113 }
114
115 // Write bytes to FileStream
116 private static void ReadCallBack(IAsyncResult asyncResult)
117 {
118 try
119 {
120 Debug.WriteLine($"SubThreadId: {Thread.CurrentThread.ManagedThreadId}");
121 // Get RequestState object
122 RequestState myRequestState = (RequestState)asyncResult.AsyncState;
123 // Get Response Stream from Server
124 Stream responserStream = myRequestState.ResponseStream;
125 int readSize = responserStream.EndRead(asyncResult);
126 if (readSize > 0)
127 {
128 myRequestState.Filestream.Write(myRequestState.BufferRead, 0, readSize);
129 responserStream.BeginRead(myRequestState.BufferRead, 0, myRequestState.BufferRead.Length, ReadCallBack, myRequestState);
130 }
131 else
132 {
133 myRequestState.Response.Close();
134 myRequestState.Filestream.Close();
135 }
136 }
137 catch (Exception e)
138 {
139 //Console.WriteLine("Error Message is:{0}", e.Message);
140 }
141 }
142 #endregion
143
144 private void btnDownLoad_Click(object sender, RoutedEventArgs e)
145 {
146 //string myDownLoafUrl = lbUrl.Content.ToString();
147 //myDownLoafUrl = "https://www.baidu.com/";
148 //myDownLoafUrl = @"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2298824648,1812234339&fm=200&gp=0.jpg";
149 DownloadFileAsync(DownLoadUrl);
150 }
151 }
152 }
<Window x:Class="Wpf_APM_BeginEnd.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_APM_BeginEnd"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Label Content="DownLoadUrl:" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Right"></Label>
<Label Content="FileSavePath:" FontSize="14" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Right"></Label>
<TextBox FontSize="20" BorderBrush="Green" BorderThickness="1" Grid.Column="1" Margin="5" Grid.Row="1" Text="{Binding FileSavePath}" Name="tbSavePath"/>
<TextBox Text="{Binding DownLoadUrl}" FontSize="20" BorderBrush="Green" BorderThickness="1" Name="lbUrl" Grid.Column="1" Margin="5"/>
<Button Content="DownLoad" Grid.Column="2" Grid.Row="2" FontSize="20" Name="btnDownLoad" Click="btnDownLoad_Click" VerticalAlignment="Top"/>
</Grid>
</Window>

三、自定义类实现APM模式
关键点:利用委托的BeginInvoke和EndInvoke

  1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4
5 namespace APMTest
6 {
7 public delegate int DequeueDataDelegate(int count);
8 public class APMHelper
9 {
10 private DequeueDataDelegate dequeueDataDelegate;
11 public DequeueDataDelegate DequeueDataDelegate
12 {
13 get
14 {
15 return dequeueDataDelegate;
16 }
17 }
18
19 public Queue<int> NumQueue = new Queue<int>();
20
21
22 public APMHelper(int count)
23 {
24 InitQueue(count);
25 }
26 public void InitQueue(int count)
27 {
28 Random random = new Random();
29 for(int i = 0; i < count; i++)
30 {
31 int myNum = random.Next(100);
32 NumQueue.Enqueue(myNum);
33 }
34
35 }
36
37 public IAsyncResult BeginDequeueData(int count, AsyncCallback callback, object state)
38 {
39 dequeueDataDelegate = DequeueData;
40 IAsyncResult ar = dequeueDataDelegate.BeginInvoke(count, callback, state);
41 return ar;
42 }
43 public int EndDequeueData(IAsyncResult ar)
44 {
45 return dequeueDataDelegate.EndInvoke(ar);
46 }
47 public int DequeueData(int count)
48 {
49 Console.WriteLine($"Func:DequeueData IsBackgroundThread:{Thread.CurrentThread.IsBackground} IsThreadPool:{Thread.CurrentThread.IsThreadPoolThread} ID:{Thread.CurrentThread.ManagedThreadId}");
50 int queueCount = NumQueue.Count;
51 int myRet = -1;
52 int myLoop = count;
53 if (queueCount >= count)
54 {
55 myRet = count;
56 }
57 else if(queueCount > 0)
58 {
59 myRet = queueCount;
60 myLoop = queueCount;
61 }
62 else
63 {
64 return -1;
65 }
66 for(int i = 0; i < myLoop; i++)
67 {
68 int ret = NumQueue.Dequeue();
69 Console.WriteLine($"Dump data:{ret}");
70 Thread.Sleep(100);
71 }
72 return myRet;
73 }
74
75 public void PrintQueue()
76 {
77 Console.WriteLine($"Func: PrintQueue IsBackgroundThread:{Thread.CurrentThread.IsBackground} IsThreadPool:{Thread.CurrentThread.IsThreadPoolThread} ID:{Thread.CurrentThread.ManagedThreadId}");
78 Console.WriteLine("Queue:");
79 foreach (var item in NumQueue)
80 {
81 Console.Write($" {item} ");
82 //Thread.Sleep(500);
83 }
84 Console.WriteLine();
85 }
86 }
87 class Program
88 {
89 static void Main(string[] args)
90 {
91 Console.WriteLine($"MainThreadId:{Thread.CurrentThread.ManagedThreadId}");
92 APMHelper apmHelper = new APMHelper(50);
93 apmHelper.PrintQueue();
94
95 //while (apmHelper.DequeueData(3) > 0)//串行执行
96 //{
97 //}
98
99 apmHelper.BeginDequeueData(3, DequeueDataCallback, apmHelper);//异步执行
100 Console.WriteLine($"******MainThread do other things...******");
101 Console.ReadLine();
102 }
103 static void DequeueDataCallback(IAsyncResult ar)
104 {
105 APMHelper apmHelper = ar.AsyncState as APMHelper;
106 int ret = -1;
107 if(apmHelper != null)
108 {
109 ret = apmHelper.EndDequeueData(ar);
110 }
111 if(ret > 0)
112 {
113 apmHelper.BeginDequeueData(3, DequeueDataCallback, apmHelper);
114 }
115 }
116 }
117 }

异步编程之APM的更多相关文章

  1. .NET异步编程之APM模式

    目录 1.AMP模式简介 2.使用BeginInvoke实现异步委托 3.原始线程怎么知道新线程已经运行完毕 4.使用AsyncCallback委托实现回调模式 5.源代码下载 shanzm-2020 ...

  2. 异步编程之Generator(1)——领略魅力

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  3. 异步编程之Promise(3):拓展进阶

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  4. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  5. (翻译)异步编程之Promise(1):初见魅力

    原文:https://www.promisejs.org/ by Forbes Lindesay 异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2) ...

  6. net异步编程之await

    net异步编程之await 初探asp.net异步编程之await   终于毕业了,也顺利进入一家期望的旅游互联网公司.27号入职.放肆了一个多月没写代码,好方啊. 另外一下观点均主要针对于await ...

  7. Javascript异步编程之setTimeout与setInterval详解分析(一)

    Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...

  8. 异步编程之co——源码分析

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  9. 异步编程之Generator(2)——剖析特性

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

随机推荐

  1. 常见内部排序算法对比分析及C++ 实现代码

    内部排序是指在排序期间数据元素全部存放在内存的排序.外部排序是指在排序期间全部元素的个数过多,不能同时存放在内存,必须根据排序过程的要求,不断在内存和外存之间移动的排序.本次主要介绍常见的内部排序算法 ...

  2. Java基础00-基础知识练习12

    1. 减肥计划 1.1 if语句实现 import java.util.Scanner; public class Demo01 { public static void main(String[] ...

  3. 【论文阅读】Motion Planning through policy search

    想着CSDN还是不适合做论文类的笔记,那里就当做技术/系统笔记区,博客园就专心搞看论文的笔记和一些想法好了,[]以后中框号中间的都算作是自己的内心OS 有时候可能是问题,有时候可能是自问自答,毕竟是笔 ...

  4. python基础之列表推导式

    #列表推导式 ---> 返回的是列表 for语句 效率更高# 1*1 2*2 3*3 4*4 5*5 6*6 7*7 8*8 9*9# import time# to = time.clock( ...

  5. CentOS 7安装Python3 笔记

    当前系统为阿里云的CentOS7.3 64位操作系统. 为了能让后续安装的软件(django,uwsgi,nginx等)尽量减少出现bug的几率,先把可能的依赖包都安装上. 一.安装依赖包 yum - ...

  6. js学习笔记之公告逐行显示

    $(function(){ var newsListHeight = $(".news-list").height(); //获得内容的高度 var newsConHeight = ...

  7. 【LeetCode】841. 钥匙和房间

    841. 钥匙和房间 知识点:图:递归 题目描述 有 N 个房间,开始时你位于 0 号房间.每个房间有不同的号码:0,1,2,...,N-1,并且房间里可能有一些钥匙能使你进入下一个房间. 在形式上, ...

  8. Matlab常用函数:二进制和十进制转换,均值,方差

    文章目录 Size s=size(A) [r,c]=size(A) [r,c,m]=size(A) size(A,n) 二进制和十进制转换 dec2bin mean 均值 mean(a,1) mean ...

  9. OpenGL学习笔记(二)画三角形

    目录 渲染管线(Graphics Pipeline) 编码实现 顶点数据 顶点缓冲对象(VBO) 顶点着色器 编译着色器 片段着色器 着色器程序 链接顶点属性 顶点数组对象 最终绘制三角形 索引缓冲对 ...

  10. 网络编程之TCP客户端开发和TCP服务端开发

    开发 TCP 客户端程序开发步骤 创建客户端套接字对象 和服务端套接字建立连接 发送数据 接收数据 关闭客户端套接字 import socket if __name__ == '__main__': ...