异步编程之APM

一、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 }

 

posted on 2019-02-13 10:59  缘惜  阅读(1079)  评论(0编辑  收藏  举报