陈希章@中国 发表于 2012-12-10 13:58:38

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

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

<div id="cnblogs_post_body">内容摘要

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

视频地址


演示代码:

完整源代码请通过这里下载:http://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>                // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.       public class SampleWebService : System.Web.Services.WebService    {            public string HelloWorld()      {            Thread.Sleep(5000);            return "Hello World";      }            public string HelloWorld2()      {            Thread.Sleep(2000);            return "Hello World 2";      }            public string HelloWorld3()      {            Thread.Sleep(2000);            return "Hello World 3";      }    }}.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }界面代码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;      }    }}.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }
页: [1]
查看完整版本: 多个异步任务串行化的思考和研究(Silverlight, AJAX场景)