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);
        }
    }
  • 相关阅读:
    呃,如何使 .NET 程序,在 64位 系统 中,以 32位 模式运行。
    [转载]Cortana 设计指导方针
    Could not load file or assembly System.Core, Version=2.0.5.0
    wpf中用户控件的属性重用
    浅谈AutoResetEvent的用法
    WPF异步载入图片,附带载入中动画
    WPFLoading遮层罩
    获取WPF的DataGrid控件中,是否存在没有通过错误验证的Cell
    WPF通过异常来验证用户输入
    WPF验证之——必填验证
  • 原文地址:https://www.cnblogs.com/wang_yb/p/2270792.html
Copyright © 2011-2022 走看看