zoukankan      html  css  js  c++  java
  • 《CLR Via C# 第3版》笔记之(二十二) APM和EAP

    APM的介绍请参见《CLR Via C# 第3版》笔记之(二十一) - 异步编程模型(APM)

    EAP是Event-based Asynchronous Pattern的缩写,指基于事件的异步模式。

    主要内容:

    • EAP和APM的比较
    • APM转换为Task
    • EAP转换为Task

    1. EAP和APM的比较

    EAP是基于事件的异步模型,比如winform中很多事件***Changing,***Changed,***Completed等等。

    这里的异步是指这些事件方法(即***Changing方法,***Completed方法等)是被异步调用的。

    但是这些事件方法执行时是同步的,比如***Changing方法执行时,UI是无响应的。

    所以我们遇到处理时间较长的操作时,应该让这些事件方法尽快返回,在事件方法中将那些耗时的操作通过其他异步方式调用(比如多线程,Task等)。

    Jeffrey大牛喜爱APM的方式的异步对EAP方式不太感冒。

    我本身觉得APM和EAP各有自己的优点,可能是应用场景不同。以下是我自己的一些理解:

     

    应用场景

    资源消耗

    回调方法

    EAP

    适合事件驱动的程序(比如GUI程序)的开发,利用EAP配合visual studio可以很方便的实现各种功能

    内部封装较多,占用资源较多

    可以在事件方法中用【+=】或【-=】注册和注销多个回调方法。

    事件触发时,所有注册的回调的方法都会被执行,动态调用回调方法较困难

    APM

    适用于服务端程序,利用Begin***和End***,可以应对大量的请求

    占用资源少

    可以动态的调用指定的回调方法

    下面用个简单的例子来展现EAP和APM是如何实现异步的。

    using System;
    using System.Windows.Forms;
    using System.Net;
    using System.Threading;
    
    class CLRviaCSharp_22
    {
        static void Main(string[] args)
        {
            MyFormEAP eap = new MyFormEAP();
            eap.ShowDialog();
    
            MyFormAPM apm = new MyFormAPM();
            apm.ShowDialog();
        }
    }
    
    internal class MyFormEAP : Form
    {
        public MyFormEAP()
        {
            Text = "(EAP)Click in the window to start a web request";
            Width = 800;
            Height = 600;
        }
    
        protected override void OnClick(EventArgs e)
        {
            // 开始异步web请求
            Text = "(EAP)web request initilized";
            var client = new WebClient();
            client.DownloadStringCompleted += ProcessString;
    
            client.DownloadStringAsync(new Uri("http://www.baidu.com"));
            base.OnClick(e);
        }
    
        private void ProcessString(object sender, DownloadStringCompletedEventArgs e)
        {
            Text = "(EAP)Content length: " + e.Result.Length;
        }
    }
    
    
    internal class MyFormAPM : Form
    {
        public MyFormAPM()
        {
            Text = "(APM)Click in the window to start a web request";
            Width = 800;
            Height = 600;
        }
    
        protected override void OnClick(EventArgs e)
        {
            // 开始异步web请求
            Text = "(APM)web request initilized";
            var webreq = WebRequest.Create("http://www.baidu.com");
            webreq.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webreq);
            base.OnClick(e);
        }
    
        private void ProcessWebResponse(IAsyncResult result)
        {
            var req = (WebRequest)result.AsyncState;
            using (var res = req.EndGetResponse(result))
            {
                Text = "Content length: " + res.ContentLength;
            }
        }
    
        private static AsyncCallback SyncContextCallback(AsyncCallback callback)
        {
            SynchronizationContext sc = SynchronizationContext.Current;
            // 如果没有同步上下文,直接返回传入的东西
            if (sc == null) return callback;
    
            // 返回一个委托,这个委托将委托Post到捕捉到的SC中
            return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
        }
    }

    从上面的例子也可以看出,在基于事件的操作上,EAP方式的代码要比APM方式简单的多。

    实现方式上,

    APM方式用Begin***和End***方式实现异步

    EAP用一个异步请求(DownloadStringAsync)和完成事件时回调函数的注册(client.DownloadStringCompleted += ProcessString;)来实现异步

    利用Task的ContinuWith方法,可以APM和EAP转换为一个Task。

    下面两节演示如何将上例中的APM方式和EAP方式转换为一个Task。

    2. APM转换为Task

    主要利用TaskFactory中的FromAsync方法。

    using System;
    using System.Windows.Forms;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    
    class CLRviaCSharp_22
    {
        static void Main(string[] args)
        {
            MyFormAPM apm = new MyFormAPM();
            apm.ShowDialog();
        }
    }
    
    internal class MyFormAPM : Form
    {
        public MyFormAPM()
        {
            Text = "(APM)Click in the window to start a web request";
            Width = 800;
            Height = 600;
        }
    
        protected override void OnClick(EventArgs e)
        {
            // 开始异步web请求
            Text = "(APM)web request initilized";
            var webreq = WebRequest.Create("http://www.baidu.com");
    
            Task.Factory.FromAsync<WebResponse>(webreq.BeginGetResponse, webreq.EndGetResponse, null)
                .ContinueWith(task =>
                {
                    var sc = SynchronizationContext.Current;
                    sc.Post(state => { Text = "(APM)Content length: " + task.Result.ContentLength; }, null);
                }, TaskContinuationOptions.ExecuteSynchronously);
            base.OnClick(e);
        }
    }

    这里有个很重要的地方,就是Task的ContinueWith方法里设置的TaskContinuationOptions.ExecuteSynchronously参数。

    刚开始我没有设置这个参数,怎么也得不到SynchronizationContext.Current(每次都是null)。所以设置不了Text属性。

    3. EAP转换为Task

    主要利用TaskCompletionSource这个类。

    using System;
    using System.Windows.Forms;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    
    class CLRviaCSharp_22
    {
        static void Main(string[] args)
        {
            MyFormEAP eap = new MyFormEAP();
            eap.ShowDialog();
        }
    }
    
    internal class MyFormEAP : Form
    {
        public MyFormEAP()
        {
            Text = "(EAP)Click in the window to start a web request";
            Width = 800;
            Height = 600;
        }
    
        protected override void OnClick(EventArgs e)
        {
            // 开始异步web请求
            Text = "(EAP)web request initilized";
            var client = new WebClient();
            var tcs = new TaskCompletionSource<string>();
    
            // DownloadStringCompleted实在GUI线程上执行的
            client.DownloadStringCompleted += (sender, ea) =>
            {
                tcs.SetResult(ea.Result);
            };
    
            // TaskContinuationOptions.ExecuteSynchronously参数保证了
            // ContinueWith也在GUI线程上
            tcs.Task.ContinueWith(task =>
            {
                Text = "(EAP)Content length: " + task.Result.Length;
            }, TaskContinuationOptions.ExecuteSynchronously);
    
            client.DownloadStringAsync(new Uri("http://www.baidu.com"));
            base.OnClick(e);
        }
    }
  • 相关阅读:
    383. Ransom Note
    598. Range Addition II
    453. Minimum Moves to Equal Array Elements
    492. Construct the Rectangle
    171. Excel Sheet Column Number
    697. Degree of an Array
    665. Nondecreasing Array
    视频网站使用H265编码能提高视频清晰度吗?
    现阶段的语音视频通话SDK需要解决哪些问题?
    企业远程高清会议平台视频会议系统在手机端使用的必备要求有哪些?
  • 原文地址:https://www.cnblogs.com/wang_yb/p/2270792.html
Copyright © 2011-2022 走看看