• 异步编程之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 }
  • 相关阅读:
    面试题: 数据库 已看1 group by 和order by的练习 sql语句练习简单 有用
    笔试题: 数据库 已看1 一些关键的sql语句练习 和选择题 有用 sql语句练习 挺好
    面试题: mysql数据库 已看1 简单的sql练习
    面试题: mysql 数据库已看 sql安全性 索引 引擎 sql优化
    面试题: redis面试题 有用 redis详细
    面试题: mysql 数据库去重 已看1 不好使
    面试题: 数据库已看1 视图存储过程 没用
    面试题: mysql数据库 已看1 索引和事务 没用
    面试题: 数据库 oracle数据库 已看1 意义不大 有用
    LeetCode--Valid Palindrome
  • 原文地址:https://www.cnblogs.com/3xiaolonglong/p/10368682.html
Copyright © 2020-2023  润新知