一、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. SQLITE数据库不支持远程访问

    SQLITE数据库不支持远程访问 import sqlite3 conn=sqlite3.connect("dailiaq.db") cur=conn.cursor() def c ...

  2. 高校表白App-团队冲刺第三天

    今天要做什么 今天开站立会议的时候,忽然发觉在一个完整的App上好像是有一些引导页的,比如说在第一次使用App的时候,或者是在使用App的时候会出现新手指引操作. 做了什么 通过查阅资料来学习View ...

  3. pycharm基础使用入门

    pycharm基础使用入门 输出 print函数 print('hello world') 右键选择run或者右上角的三角形运行,可以运行出结果 "E:\all sorts of learn ...

  4. 04 AOF日志:宕机了,Redis如何避免数据丢失

    接下来两篇将记录Redis持久化存储两大技术:AOF日志.RDB快照 本篇重点 "AOF日志实现""AOF日志三种写回策略""AOF重写--避免日志过 ...

  5. P3209-平面图判定

    平面图 平面图就是所有点的连边不相交的图.(当然是在你尽量想让它不相交的情况下).这一点可以大概理解成拓扑图的性质,即每连一条边就会将某个区域进行分割--很明显,如果两个点分别处在两个不可达的区域,它 ...

  6. [考试总结]noip模拟13

    因为最近考试频繁,所以咕掉了好长时间... 淦,刚说完又来一场... 先咕了,等以后有时间再写.... 回来了... 首先看到这个题目们,感觉就不存好意... 然后开始开 \(T1\). 只能蒻蒻地按 ...

  7. WEB安全新玩法 [10] 防范竞争条件支付漏洞

    服务器端业务逻辑,特别是涉及数据库读写时,存在着关键步骤的时序问题,如果设计或代码编写不当就可能存在竞争条件漏洞.攻击者可以利用多线程并发技术,在数据库的余额字段更新之前,同时发起多次兑换积分或购买商 ...

  8. tomcat与springmvc 结合 之---第17篇 StandContext容器和SpringMVC的WebApplicationContext的联系

    writedby 张艳涛, 上一篇分析了,dispatcherservlet通过getServletConfig 方法获取了web.xml定义的<param-init>属性的过程 那么在如 ...

  9. SpringMVC 参数中接收之一 List

    作者:张艳涛 time:2020-07-31 SpingMVC 一.前台传数组,SpingMVC用addusers(@RequestBody List<UserPojo> userlist ...

  10. WIN XP SP2系统经常性死机问题解决历程

    如题: 1.初始时,XP还能进入系统,等系统3分钟左右,鼠标熄灭,键盘无反应,查看资源管理器CPU 100%,内存占用不高. 2.现象初步分析: a.怀疑是病毒占用CPU 100%,于是下载360安全 ...