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);
        }
    }
  • 相关阅读:
    MySql cmd下的学习笔记 —— 引擎和事务(engine,transaction)
    MySql cmd下的学习笔记 —— 有关视图的操作(algorithm)
    MySql cmd下的学习笔记 —— 有关视图的操作(建立表)
    MySql cmd下的学习笔记 —— 有关常用函数的介绍(数学函数,聚合函数等等)
    MySql cmd下的学习笔记 —— 有关多表查询的操作(多表查询练习题及union操作)
    MySql 在cmd下的学习笔记 —— 有关多表查询的操作(内连接,外连接,交叉连接)
    MySql cmd下的学习笔记 —— 有关子查询的操作(where型,from型,exists型子查询)
    MySql cmd下的学习笔记 —— 有关select的操作(order by,limit)
    剑指Offer--第21题 调整数组顺序使奇数位于偶数前面;
    剑指Offer--和为s的连续正数序列
  • 原文地址:https://www.cnblogs.com/wang_yb/p/2270792.html
Copyright © 2011-2022 走看看