• WCF异步调用


    添加引用服务--高级--选中 生产异步操作

    服务端接口操作

    [OperationContract]
    int Add(int a, int b);

    客户端:

    引用服务:在引用服务时,左下角点击“高级”按钮,勾选“生成异步操作”即可。

     ServiceReference1.Service1Client   client= new ServiceReference1.Service1Client();
    //int result = client.Add(5, 7); // 同步方法
    client.AddCompleted += client_AddCompleted;
    client.AddAsync(3, 2); // 异步方法

    //另外一种异步调用方式:BeginXXX和EndXXX的方法

    private void client_AddCompleted(object sender, AddCompletedEventArgs e)
    {
    try
    {
    int result = e.Result;

    CalculateClient client = (CalculateClient)sender;
    if (client != null)
    {
    // 释放资源。
    client.Close();
    }
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.Message);
    }
    }

    WCF入门(二)——异步操作

    WCF是网络编程,既然是网络编程,那么就一般离不开异步操作了。异步操作按照发生源可以分为客户端异步和服务器端异步,本文就分别简单的介绍一下该如何实现。

    一、客户端异步

    以一个简单的服务为例:

        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            string Foo();
        }

    这个服务不用输入,仅仅返回一个字符串,通常的时候,我们喜欢和调用本地函数方式类似的以调用WCF服务,并且同步等待至远程返回结果。

        var client = new WcfClient.Service.Service1Client();
        var result = client.Foo();
        Console.WriteLine(result);

    一般来说,这个并没有什么问题;但是,和调用本地函数一样,如果该函数需要将长时间才能返回结果,则会阻塞线程,如果调用线程是UI线程的话则会导致UI没有响应。特别是像WCF这种远程调用的方式更会由于网络原因等导致耗时较长。

    按照本地耗时函数的处理方式,我们可以借助Task来处理这个等待,然后获取返回值后,再通知界面。

        var client = new WcfClient.Service.Service1Client();
        var result = await Task.Run(() => client.Foo());
        Console.WriteLine(result);

    这个方式虽然不阻塞界面,但是消耗了一个Task在同步等待,效率不高。实际上,对于这种网络操作,VisualStudio在生成本地客户端代码的时候,就生成了异步方式访问的接口。

      

    直接使用这个异步接口,就可以实现IO异步的方式访问,不用新开启一个Task来同步等待。

        var client = new WcfClient.Service.Service1Client();
        var result = await client.FooAsync();
        Console.WriteLine(result);

    关于默认的异步访问接口,也可以在根据服务生成本地代码的时候选择使用传统的BeginFoo,EndFoo的那种形式(由于远不如Task式的异步访问好用,不推荐)。

    二、服务器端异步

    服务器端异步指的主要是服务的异步实现,

    以一个天气预报的WCF服务为例,假如我们要实现一个这样的接口:

        public interface IService1
        {
            string QueryWeather(string city);
        }

    由于我本地并没有天气预报的数据,要实现一个这样的服务,必须得到气象网站去查询,按照传统的同步方式,则实现如下:

        public class Service1 : IService1
        {
            public string QueryWeather(string city)
            {
                var queryTask = QueryWeatherFromRemoteSite(city);
                queryTask.Wait();
                return queryTask.Result;
            }

            static Task<string> QueryWeatherFromRemoteSite(string city)
            {
                //省略实现
            }
        }

     很明显,每次一次处理QueryWeather的时候,都得阻塞当前线程至远程查询的完成,极大的浪费了系统资源。需要改成异步的方式实现。

    传统的异步实现

    在.Net 4.5前,要异步实现QueryWeather函数,必须得把接口声明成如下形式:

        [ServiceContract]
        public interface IService1
        {
            [OperationContract(AsyncPattern = true)]
            IAsyncResultBeginQueryWeather(string city, AsyncCallback callback, object asyncState);
            string EndQueryWeather(IAsyncResult result);
        }

    可以看到,和同步方式的契约声明不同的是:

    1. 在接口名称前加了一个Begin
    2. 设置了AsyncPattern = true

    然后就要实现这个接口了,如下是一个简单的示例:

        public class Service1 : IService1
        {
            #region IService1 成员

            public IAsyncResult BeginQueryWeather(string city, AsyncCallback callback, object asyncState)
            {
                return new CompletedAsyncResult<string>(QueryWeatherFromRemoteSite(city));
            }

            public string EndQueryWeather(IAsyncResult result)
            {
                return result.AsyncState as string;
            }

            #endregion

            static Task<string> QueryWeatherFromRemoteSite(string city)
            {
                //这里只是一个桩函数,表示的是一个异步方法
                return Task.FromResult("晴");
            }
        }

        class CompletedAsyncResult<T> : IasyncResult
        {
            Task<T> task;
            ManualResetEvent waitHandle = new ManualResetEvent(false);

            public CompletedAsyncResult(Task<T> task)
            {
                this.task = task;
                task.ContinueWith(_ => waitHandle.Set());
            }

            #region IAsyncResult Members

            public object AsyncState { get { return task.Result; } }
            public WaitHandle AsyncWaitHandle { get { return waitHandle; } }
            public bool CompletedSynchronously { get { return true; } }
            public bool IsCompleted { get { return task.IsCompleted; } }

            #endregion
        }

    编译运行后,从TestClient上来看,生成的接口并没有带上前面的Begin,从其发布的Wsdl来看,和之前的同步实现没有区别,也就是说:客户端是感知不到服务器端是如何实现的。

      

    更加简单的异步实现:

    从上面的实现来看,实现异步操作是要实现两个接口,BeginXXX和EndXXX,这个是非常麻烦的,在.Net 4.5里,我们可以通过async语法糖来简化这一过程。

    首先还是来看看接口声明:

        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            Task<string> QueryWeatherAsync(string city);
        }

    和同步方式的契约声明也有两点区别:

    1. 返回值不是string,而是Task<string>
    2. 接口名称中加了Async后缀(这个其实不是必要的,但是为了良好的编程习惯,建议加上)。

    既然接口就定义除了Task类型的返回值,那么实现就简单了:

        public class Service1 : IService1
        {
            #region IService1 成员

            public Task<string> QueryWeatherAsync(string city)
            {
                return QueryWeatherFromRemoteSite(city);
            }

            #endregion

            static Task<string> QueryWeatherFromRemoteSite(string city)
            {
                //这里只是一个桩函数,表示的是一个异步方法
                return Task.FromResult("晴");
            }
        }

    比前面的BeginXXX的方式来说,简单且简洁了不少。

    编译运行后,从TestClient上来看,生成的接口并没有带上结尾的Async,非常人性化。

      

    三、小结:

    在WCF架构中,服务器端不感知客户端以同步还是异步的方式来访问,客户端也感知不到服务器端是同步还是异步方式来实现服务的,这个也是一种松耦合的体现。

    至于实现异步操作的实现:

      1. 客户端直接通过代码生成器可以同时生成异步步访问的接口,无需自己编写代码。
      2. 服务器端可以通过async和Task来简化异步接口的实现。
  • 相关阅读:
    【C/C++】散列/算法笔记4.2
    【C/C++】PAT A1025 Ranking/算法笔记
    【科研工具】CAJViewer的一些操作
    【科研工具】知云-外文文献翻译神器
    【科研工具】流程图软件Visio Pro 2019 详细安装破解教程
    【Word】自动化参考文献-交叉引用
    【Matlab】线性调频信号LFM 仿真
    不是人人都懂的学习要点
    linux的那些事
    从一个男人身上看出他的修养和抱负
  • 原文地址:https://www.cnblogs.com/Alex80/p/7356898.html
Copyright © 2020-2023  润新知