一、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. springMVC-9-异常处理器和拦截器

    异常解析器: 用于统一处理 servlet 中的异常; 拦截器: 用于统一处理业务中需要统一处理的页面(比如登录判断等), 可抽取出来统一处理. 我们一般需要在每个页面都通过在session中寻找有无 ...

  2. 微信小程序云开发-云存储-上传文件(图片/视频)到云存储 精简代码

    说明 图片/视频这类文件是从客户端会话选择文件. 一.wxml文件添加if切换显示 <!--上传文件到云存储--> <button bindtap="chooseImg&q ...

  3. CF896D Nephren Runs a Cinema

    CF896D Nephren Runs a Cinema 题意 售票员最开始没有纸币,每次来一个顾客可以给她一张.拿走她一张或不操作.求出不出现中途没钱给的情况 \(n\) 名顾客后剩余钱数在 \(l ...

  4. 初识Stream API + Lambda表达式

    使用新特性简化代码,增强可读性 package com.gg.java8; import java.util.*; import org.junit.Test; public class TestLa ...

  5. [考试总结]noip模拟7

    为啥博客园 \(\LaTeX\) 老挂???! \(\huge{\text{菜}}\) 刚开始写 \(T1\) 的时候,在看到后缀前缀之后,直接想到 \(AC\) 自动机,在画了半个 \(trie\) ...

  6. vscode配置java+gradle开发环境

    1.安装扩展包Java Extension Pack,里面包含java开发所必须的扩展 2.安装java jdk,8版本就是1.8版本,根据需要安装不同的版本 3.下载gradle,将bin文件夹添加 ...

  7. 用webpack发布一个vue插件包

    创建库 本来以为很简单,结果配置了webpack之后,运行build就报错了,似乎不认识es6语法,于是先后安装了几个包: @babel/core @babel/preset-env babel-lo ...

  8. 使用C#winform编写渗透测试工具--端口扫描

    使用C#winform编写渗透测试工具--端口扫描器 主要介绍使用C#winform编写渗透测试工具--端口扫描器,端口扫描器则是一种检测服务器或者主机虚拟端口是开启或关闭的工具.由于连接到局域网或互 ...

  9. Linux[Manjaro] 小新15笔记本AMD ryzen锐龙4800U,在安装系统后出现的随即死机冻屏问题

    Linux[Manjaro] 小新15AMD ryzen锐龙4800U,在安装系统后出现的随即死机冻屏问题解决办法 年初尝试将manjaro安装在我的笔记本上就存在这个问题,也一度将我劝退.系统安装在 ...

  10. spingsecurity 前后端分离跨域,ajax无用户信息

    1.自测时用的postman没有任何问题 2.和前端对接时发现登录不上,ajax Error 出错:{"readyState":0,"responseText" ...