• 多个异步任务串行化的思考和研究(Silverlight, AJAX场景)


    内容摘要

    异步编程在我们日常的开发工作中经常遇到的场景。现在的应用程序,很难说不需要进行异步的一些任务。例如网络服务的调用。典型的情况就在于Silverlight和AJAX场景中。

    一个异步任务的执行,可能很简单,.NET有大约4种所谓的异步编程模型,最常见的是基于回调的方式。每个异步任务,都是独立的一个线程,这些任务之间,默认情况下不会有依赖,也不会有先后顺序的概念的。他们一般是同时发出去的请求,然后根据具体每个任务的情况,会逐渐返回结果。但这里有一个情况就是,他们返回结果的时间是不可预期的。

    但是如果我们需要有多个异步任务,而且这些任务之间本身存在一定的先后次序,例如A先执行完,然后才能执行B,甚至A的结果要作为B的输入。那么这个时候应该怎么做呢?

    在.NET内部的实现中,可以通过在任务之间互相嵌套的方式简单地实现,但其代码向当地不易于阅读和扩展。

    为此,我们做了一些封装和改进。本次演讲,我首先讲解了默认的一些实现方式,然后着重演示了两种扩展

    1.使用AsyncTaskFactory 这个组件 http://nuget.org/packages/AsyncTaskFactory

    2.使用AsyncCTP这个组件:http://www.microsoft.com/en-us/download/details.aspx?id=9983

    本次演讲的最后,还讲解了如何在AJAX应用中使用队列的方式来实现多个异步任务的串行化。

    讲义地址

    http://sdrv.ms/Sw7ELi

    视频地址

    演示代码:

    完整源代码请通过这里下载:https://files.cnblogs.com/chenxizhang/AsyncQueueSample.zip (需要Visual Studio 2010+Silvelight 4.0 Toolkit)

    请结合视频理解代码

    服务代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Threading;
    
    namespace AsyncQueueSample.Web
    {
        /// <summary>
        /// 这个例子用来演示多个异步任务的串行化,这里只是简单地模拟了一个长时间执行的方法,使用线程休眠
        /// 作者:陈希章
        /// 时间:2012年9月
        /// 反馈:ares@xizhang.com
        /// </summary>
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [System.ComponentModel.ToolboxItem(false)]
        // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
        [System.Web.Script.Services.ScriptService]
        public class SampleWebService : System.Web.Services.WebService
        {
    
            [WebMethod]
            public string HelloWorld()
            {
    
                Thread.Sleep(5000);
                return "Hello World";
            }
    
    
            [WebMethod]
            public string HelloWorld2()
            {
    
                Thread.Sleep(2000);
                return "Hello World 2";
    
            }
    
            [WebMethod]
            public string HelloWorld3()
            {
    
                Thread.Sleep(2000);
                return "Hello World 3";
    
            }
        }
    }
    
     
     
    界面代码
    using System;
    using System.Linq;
    using System.ServiceModel.DomainServices.Client;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace AsyncQueueSample
    {
        /// <summary>
        /// 这个例子演示了如何实现多个异步任务的串行化
        /// 作者:陈希章
        /// 时间:2012年9月
        /// 反馈:ares@xizhang.com
        /// </summary>
        public partial class MainPage : UserControl
        {
            public MainPage()
            {
                InitializeComponent();
            }
    
    
            /// <summary>
            /// 这是常规的并行方式,多个异步调用彼此是没有关联的,同时发出请求
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btASync_Click(object sender, RoutedEventArgs e)
            {
                var proxy = new localhost.SampleWebServiceSoapClient();
                proxy.HelloWorldCompleted += (o, a) => { MessageBox.Show(a.Result); };
                proxy.HelloWorldAsync();//发出异步请求
    
                proxy.HelloWorld2Completed += (o, a) => { MessageBox.Show(a.Result); };
                proxy.HelloWorld2Async();
    
    
            }
            /// <summary>
            /// 这是常规实现的串行方式,在任务之间手工实现顺序,耦合性很高
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btNormal_Click(object sender, RoutedEventArgs e)
            {
                var proxy = new localhost.SampleWebServiceSoapClient();
                proxy.HelloWorldCompleted += (o, a) =>//第一个任务的回调
                {
                    MessageBox.Show(a.Result);
    
                    proxy.HelloWorld2Completed += (o1, a1) =>//第二个任务的回调
                    {
                        MessageBox.Show(a1.Result);
    
                        proxy.HelloWorld3Completed += (o2, a2) =>
                        {
                            MessageBox.Show(a2.Result);
                        };
    
                        proxy.HelloWorld3Async();
    
                    };
    
                    proxy.HelloWorld2Async();
                };
    
                proxy.HelloWorldAsync();
            }
    
            /// <summary>
            /// 使用AsyncTaskFactory改进的做法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btAsyncTaskFactory_Click(object sender, RoutedEventArgs e)
            {
                var proxy = new localhost.SampleWebServiceSoapClient();
                var task1 = new AsyncAction("task 1");
                task1.SetAction(() =>
                {
                    proxy.HelloWorldCompleted += (o, a) => { MessageBox.Show(a.Result); task1.OnCompleted(); };
                    proxy.HelloWorldAsync();
                });
    
                var task2 = new AsyncAction("task 2");
                task2.SetAction(() =>
                {
                    proxy.HelloWorld2Completed += (o, a) => { MessageBox.Show(a.Result); task2.OnCompleted(); };
                    proxy.HelloWorld2Async();
                });
    
    
                var task3 = new AsyncAction("task 3");
                task3.SetAction(() =>
                {
                    proxy.HelloWorld3Completed += (o, a) => { MessageBox.Show(a.Result); task3.OnCompleted(); };
                    proxy.HelloWorld3Async();
                });
    
    
                var runner = new AsyncActionRunner(task1, task3, task2);
                runner.Execute();
            }
    
            /// <summary>
            /// 使用AsyncCTP改进的做法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            async private void btAsyncCTP_Click(object sender, RoutedEventArgs e)
            {
                var proxy = new localhost.SampleWebServiceSoapClient() as localhost.SampleWebServiceSoap;
    
                var result1 = await Task<localhost.HelloWorldResponse>.Factory.FromAsync(
                    proxy.BeginHelloWorld(new localhost.HelloWorldRequest(), null, null),
                    proxy.EndHelloWorld);
    
                MessageBox.Show(result1.Body.HelloWorldResult);
    
                var result3 = await Task<localhost.HelloWorld3Response>.Factory.FromAsync(
                    proxy.BeginHelloWorld3(new localhost.HelloWorld3Request(), null, null),
                    proxy.EndHelloWorld3);
                MessageBox.Show(result3.Body.HelloWorld3Result);
    
                var result2 = await Task<localhost.HelloWorld2Response>.Factory.FromAsync(
                    proxy.BeginHelloWorld2(new localhost.HelloWorld2Request(), null, null),
                    proxy.EndHelloWorld2);
                MessageBox.Show(result2.Body.HelloWorld2Result);
    
            }
    
            /// <summary>
            /// 标准的RIA Service调用的做法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btRIAService_Click(object sender, RoutedEventArgs e)
            {
                var ctx = new Web.SampleDomainContext();
                ctx.Load<Web.Employee>(ctx.GetEmployeesQuery(), result =>
                {
                    MessageBox.Show(result.Entities.FirstOrDefault().FirstName);
                }, true);
    
                MessageBox.Show("加载完成");
            }
    
            /// <summary>
            /// 使用AsyncCTP改进的RIA Service调用的做法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            async private void btAsyncCTPRIA_Click(object sender, RoutedEventArgs e)
            {
                var ctx = new Web.SampleDomainContext();
                var result = await ctx.Load<Web.Employee>(ctx.GetEmployeesQuery()).AsTask();
                MessageBox.Show(result.Entities.FirstOrDefault().FirstName);
    
    
    
                MessageBox.Show("加载完成");
            }
    
        }
    
        /// <summary>
        /// 对于RIA Service的一个扩展方法
        /// </summary>
        public static class OperationExtensions
        {
            public static Task<T> AsTask<T>(this T operation)
              where T : OperationBase
            {
                TaskCompletionSource<T> tcs =
                  new TaskCompletionSource<T>(operation.UserState);
    
                operation.Completed += (sender, e) =>
                {
                    if (operation.HasError && !operation.IsErrorHandled)
                    {
                        tcs.TrySetException(operation.Error);
                        operation.MarkErrorAsHandled();
                    }
                    else if (operation.IsCanceled)
                    {
                        tcs.TrySetCanceled();
                    }
                    else
                    {
                        tcs.TrySetResult(operation);
                    }
                };
    
                return tcs.Task;
            }
        }
    
    }
    
  • 相关阅读:
    alpha版本冲刺总结
    近两天项目冲刺
    关于微软必应词典客户端 的案例分析
    第三次作业——结队编程
    hdu 1002 A + B Problem II(大数)
    ZOJ 3805 Machine(二叉树,递归)
    hdu 4704 sum(费马小定理+快速幂)
    欧拉图
    hdu 1116 Play on Words(欧拉通路)
    前50个斐波那契数
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2691474.html
Copyright © 2020-2023  润新知