一、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. Kubernetes全栈架构师(二进制高可用安装k8s集群扩展篇)--学习笔记

    目录 二进制Metrics&Dashboard安装 二进制高可用集群可用性验证 生产环境k8s集群关键性配置 Bootstrapping: Kubelet启动过程 Bootstrapping: ...

  2. Python语言的技术领域

    第一部分:各个领域应用的语言 大家看这个内容,其实你很明显发现,其实各个语言都有他的用处.我们可以说Python是应用最广的.但是暂时还是不能说它是全能的,因为他也有它的短板,但是对于一般的小公司和小 ...

  3. 【LeetCode】283.移动零

    283.移动零 知识点:数组:双指针: 题目描述 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例 输入: [0,1,0,3,12] 输出: [1, ...

  4. GraphQL 概念入门

    GraphQL 概念入门 Restful is Great! But GraphQL is Better. -- My Humble Opinion. GraphQL will do to REST ...

  5. 扩展中国剩余定理(exCRT)

    我 tm--CRT 没看懂 exCRT 却看懂了--emmmm-- 而且这名字完全就是国内的 OI 带师胡起的吧-- 考虑一次同余方程组 \[\begin{cases} x \equiv a_1\ ( ...

  6. Oracle常用SQL语句大全

    常用Oracle数据库SQL语句汇总. 1.常用操作 --清空回收站purge recyclebin;--查询回收站select * from recyclebin--查询Oracle版本信息sele ...

  7. C++五十一篇 -- VS2017开发人员新闻无法联网

    参考链接:https://blog.csdn.net/zz1589275782/article/details/88364983 这几天玩了下以前的电脑,本来想更新一下Visual Studio In ...

  8. (java4)什么是计算机

    (java4)什么是计算机 computer : 全称电子计算机,俗称电脑 能够按照程序运行.自动.高速处理海量数据的现代化智能电子设备 由硬件和软件组成 常见的由台式计算机,笔记本计算机,大型计算机 ...

  9. 用python将word转pdf、doc转docx等

    word ==> pdf def doc2pdf(file_path): """ word格式转换doc|docx ==> pdf :return: &quo ...

  10. ms17-010

    永恒之蓝和ms17-010简介: 永恒之蓝(EternalBLUE)"是Shadow Brokers(影子经纪人)黑客组织公布的一款黑客工具,该工具利用的漏洞也被称为MS17-010漏洞,M ...