C# 我可以忽略WCF异步EndXXX调用吗?

C# 我可以忽略WCF异步EndXXX调用吗?,c#,multithreading,performance,wcf,asynchronous,C#,Multithreading,Performance,Wcf,Asynchronous,我有2个服务为WCF呼叫提供服务。从客户端,我向两个服务发送相同的异步WCF BeginXXX调用,然后开始使用WaitHandle.waitHandles(waitHandles)等待答复,其中waitHandles是来自2个BeginXXX调用返回的IAsyncResults的waitHandles数组 我只想使用响应速度更快的服务的回复,即当WaitHandle.WaitAny返回索引时,我只调用带有相应IAsyncResult的EndXXX以获得更快的结果。我从不给另一端打电话 我这样做

我有2个服务为WCF呼叫提供服务。从客户端,我向两个服务发送相同的异步WCF BeginXXX调用,然后开始使用WaitHandle.waitHandles(waitHandles)等待答复,其中waitHandles是来自2个BeginXXX调用返回的IAsyncResults的waitHandles数组

我只想使用响应速度更快的服务的回复,即当WaitHandle.WaitAny返回索引时,我只调用带有相应IAsyncResult的EndXXX以获得更快的结果。我从不给另一端打电话

我这样做的原因是,有时一个服务在垃圾收集中使用了几秒钟,并且不能快速响应。根据我的经验,这两个服务通常在不同的时间进行垃圾收集,因此其中一个服务几乎总是能够快速返回答案。我的客户端应用程序非常耗时,我需要在几毫秒内得到答案

我的问题是:

  • 对于应答较慢的其他服务,我可以安全地忽略调用EndXXX方法吗?我对较慢的结果不感兴趣,但希望尽快使用较快的结果。根据我的实验,即使我没有调用EndXXX方法来获得相应的较慢的BeginXXX异步结果,似乎也没有什么不好的事情发生

  • 有人能给我解释一下当我没有为相应的BeginXXX调用EndXXX时会发生什么吗?在VisualStudio的debugger下,我似乎能够看到另一个答案是通过I/O完成端口在.NET framework中处理的,而这个处理不是来自调用EndXXX的客户端。我似乎没有因为没有进行EndXXX调用而出现任何内存泄漏。我假定所有涉及的对象都是垃圾收集的

  • 服务器端方法XXX实现是单个同步XXX还是显式异步BeginXXX/EndXXX对有什么区别吗

  • IMHO同步XXX方法实现将始终返回 需要在某个地方处理。它发生在客户端还是服务器上 当我打电话给EndXXX失败时,我的情况是什么

  • 使用WaitHandles是等待最快结果的一种好的、最有效的方法吗

  • 如果我每次都要打电话给EndXXX,我发出的XXX会让事情变得很尴尬。我必须将无趣的EndXXX调用委托给另一个线程,该线程只会忽略结果。在我的原始线程中调用所有EndXXX调用将无法以同步方式获得和使用更快的答案


  • 取决于调用开始/结束模式的对象。有些已知泄漏。从CLR通过杰弗里·里克特的C#:

    您必须调用Endxxx,否则将泄漏资源。CLR分配一些 启动异步操作时的内部资源。如果Endxxx 如果从未调用,则仅当 进程终止

  • 文档中说您必须调用end方法。如果你违反了文件的要求,你就处于未定义的行为状态。资源可能泄漏。也许他们只是在负载下这么做,谁知道呢
  • 我不知道,对不起。我只是给出了部分答案。我的建议是:实现一个不做任何事情的服务方法,并在一个循环中调用它10万次。资源是否泄漏?如果是,你有你的答案
  • 不,服务器和客户端是独立的。服务器可以是同步的,客户端可以是异步的,反之亦然。两个人甚至都无法区分另一个人做了什么。这两个服务由TCP和定义良好的协议分开。客户机甚至不可能知道服务器在做什么。服务器甚至不必使用.NET
  • 我不知道你在问什么。在幕后,WCF客户端使用TCP。传入数据将在“某处”(实际上是在线程池上)进行处理
  • 如果您的代码基本上是同步的,那么这就是您所能做的最好的事情。您将烧掉一个线程,等待N个异步服务调用。没关系
  • 为什么不在
    BeginXXX
    中指定一个回调函数,它只调用
    EndXXX
    这样您就可以始终调用
    EndXXX
    并遵循框架的使用方式。您仍然可以使用等待句柄

  • AFAIK基于任务的模式使用线程池来处理其工作。 我的客户每秒打数千个电话,完全可以 扔掉线程池

    如果您使用
    Task.Run
    Task.Factory.StartNew
    ,则情况会如此。本身,
    Task.Factory.fromsync
    不会显式创建或切换线程

    回到你的场景:

    我只想使用回答更快的服务的回复, i、 e.当WaitHandle.WaitAny返回索引时,我只调用EndXXX 使用相应的IAsyncResult以获得更快的结果。我不 有没有给对方打过电话

    让我们为
    BeginXXX/EndXXX
    异步服务调用创建
    Task
    包装器:

    public static class WcfExt
    {
        public static Task<object> WorkAsync(this IService service, object arg)
        {
            return Task.Factory.FromAsync(
                 (asyncCallback, asyncState) =>
                     service.BeginWork(arg, asyncCallback, asyncState),
                 (asyncResult) =>
                     service.EndWork(asyncResult), null);
        }
    }
    
    这与:

    var task = CallBothServicesAsync(service1, service2);
    task.Wait();
    Console.WriteLine("Result: " + task.result);
    
    此代码将阻止当前线程,与原始场景中的
    WaitHandle.WaitAny
    类似

    现在,也不推荐使用这样的阻塞,因为这样会失去异步编程模型的优势,损害应用程序的可伸缩性。被阻止的线程可能正在做一些其他有用的工作,而不是等待,例如,在web应用程序的情况下,它可能正在服务另一个传入的客户端请求

    理想情况下,您的逻辑应该是“一路异步”,直到某个根入口点。例如,使用控制台应用程序:

    static async Task CoreLoopAsync(CancellationToken token)
    {
        using(var service1 = CreateWcfClientProxy())
        using(var service2 = CreateWcfClientProxy())
        {
            while (true)
            {
                token.ThrowIfCancellationRequested();
                var result = await CallBothServicesAsync("data");
                Console.WriteLine("Result: " + result);
            }
        }
    }
    
    static void Main()
    {
        var cts = CancellationTokenSource(10000); // cancel in 10s
        try
        {
            // block at the "root" level, i.e. inside Main
            CoreLoopAsync(cts.Token).Wait();
        }
        catch (Exception ex)
        {
            while (ex is AggregatedException)
                ex = ex.InnerException;
            // report the error
            Console.WriteLine(ex.Message);
        }
    }
    

    您的目标框架版本是什么?也许您可以使用基于异步
    Task
    的模式,它将负责
    EndXXX
    :AFAIK基于任务的模式使用线程池来处理其工作。我的客户每秒打几千个电话
    var task = CallBothServicesAsync(service1, service2);
    task.Wait();
    Console.WriteLine("Result: " + task.result);
    
    static async Task CoreLoopAsync(CancellationToken token)
    {
        using(var service1 = CreateWcfClientProxy())
        using(var service2 = CreateWcfClientProxy())
        {
            while (true)
            {
                token.ThrowIfCancellationRequested();
                var result = await CallBothServicesAsync("data");
                Console.WriteLine("Result: " + result);
            }
        }
    }
    
    static void Main()
    {
        var cts = CancellationTokenSource(10000); // cancel in 10s
        try
        {
            // block at the "root" level, i.e. inside Main
            CoreLoopAsync(cts.Token).Wait();
        }
        catch (Exception ex)
        {
            while (ex is AggregatedException)
                ex = ex.InnerException;
            // report the error
            Console.WriteLine(ex.Message);
        }
    }