一、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++泛型编程-举例

    就是C++里面说的函数模板和类模板,我们以前写C语言的时候,不同的类型参数,可能要写不同的函数. C++的模板出现之后,就可以实现函数模板,函数模板可以接纳不同的类型,然后这些类型都可以调用同一个函数 ...

  2. ELK多索引配置(filebeat)

     nginx日志收集: ​ ​ #-------------------------输入 filebeat.inputs: ​ #----------json日志---------- ​ - type ...

  3. 一行代码打印python之禅

    就这一句: import this 输出: The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is be ...

  4. PLICP

    介绍 PLICP相比较于普通ICP算法,使用点线之间的距离作为度量,最终找到一个最小化该度量的闭式解(解析解). 最优结果以平方的速度收敛.相比较于ICP,IDC,MBICP.PLICP更加准确,且需 ...

  5. 《鸟哥Linux私房菜》 全套视频和PDF资料—— 老段带你学鸟哥Linux视频课程

    <鸟哥的Linux私房菜-基础篇-服务器篇(第三版)>学习Linux极为经典的入门资料,但是还是很多同学难以坚持系统的看完整本书,最终以放弃而告终. 为了帮助大家更容易入门Linux,老段 ...

  6. 【LeetCode】142. 环形链表 II

    142. 环形链表 II 知识点:链表:set:快慢指针 题目描述 给定一个链表,判断链表中是否有环. 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表 ...

  7. 03_Nginx支持SSL

    1.申请证书 https://freessl.cn/ 2.创建证书 3.离线生产 4.下载Keymanager https://keymanager.org/ 5.打开生产密钥 6.DNS验证 进入域 ...

  8. [编译] 8、在Linux下搭建 stm8 单片机的开发烧写环境(makefile版)

    目录 一.SDCC(Small Device C Compiler)编译环境搭建 1.1.下载 1.2.编译 1.3.测试 二.Hex2Bin+命令行烧写工具配置使用 2.1.下载工具安装配置 2.2 ...

  9. Thinkphp 生成的验证码不显示问题解决

    在调用验证码之前加上   ob_clean(); 将: public function verify(){                $verify = new \Think\Verify();  ...

  10. Linux扩展分区和文件系统

    磁盘分区 linux也与windows一样,为了使用全部的磁盘空间,需要先对磁盘分区:如果所有分区的总容量小于磁盘容量,说明磁盘还有未分配空间,这个时候会对磁盘造成浪费.需要增加一个新的分区来将全部空 ...