5

I have 2 services servicing WCF calls. From a client I send the same asynchronous WCF BeginXXX call to both services and then start waiting for replies with a WaitHandle.WaitAny(waitHandles) where waitHandles is an array of WaitHandles from the IAsyncResults returned by the 2 BeginXXX calls.

I want to use only the reply from the service that answers faster, i.e. when WaitHandle.WaitAny returns with an index I only call EndXXX with the corresponding IAsyncResult to get the faster result. I don't ever call the other EndXXX.

My reason for doing this is that sometimes a service uses several seconds in garbage collection and is not able to answer fast. According to my experiences the 2 services do garbage collections usually in different times so one of them is almost always capable of returning a fast answer. My client application is very time critical, I need an answer within a few milliseconds.

My questions are:

  1. Can I safely ignore calling EndXXX method for the other service that was slower in answering? I am not interested in the slower result but want to use the faster result ASAP. According to my experiments nothing bad seems to happen even if I don't call the EndXXX method for the corresponding slower BeginXXX async result.

  2. Would somebody mind explaining to me what exactly happens when I don't make an EndXXX call for a corresponding BeginXXX? Under debugger in Visual Studio I seem to able to see that another answer is processed in the .NET framework via an I/O completion port and this processing does not originate from my client calling EndXXX. And I don't seem to have any memory leaks because of not making the EndXXX call. I presume all objects involved are garbage collected.

  3. Does it make any difference whether the server side method XXX implementation is a single synchronous XXX or an explicit asynchronous BeginXXX/EndXXX pair?

  4. IMHO a synchronous XXX method implementation will always return an answer that needs to be handled somewhere. Does it happen on client or server side in my case when I fail to call EndXXX?

  5. Is using the WaitHandles a good and most efficient way of waiting for the fastest result?

  6. If I have to call EndXXX for each BeginXXX I have sent out makes things quite awkward. I would have to delegate the uninteresting EndXXX call into another thread that would just ignore the results. Calling all EndXXX calls in my original thread would defeat the purpose of getting hold of and using the faster answer in a synchronous manner.

aku
  • 103
  • 4
  • What is the Framework version you're targeting? Perhaps you can use asynchronous `Task`-based pattern, which would take care of `EndXXX`: http://stackoverflow.com/a/21498956/1768303 – noseratio Mar 22 '14 at 02:53
  • AFAIK the Task-based pattern uses the thread pool to handle its work. My client makes thousands of calls per second and would completely trash the thread pool. – aku Mar 22 '14 at 09:59
  • I addressed your concern in my [answer](http://stackoverflow.com/a/22585885/1768303), @aku. – noseratio Mar 23 '14 at 01:22

3 Answers3

5
  1. The documentation says that you have to call the end method. If you violate what the docs demand you are in undefined behavior land. Resources can leak. Maybe they just do so under load, who knows?
  2. I don't know, sorry. I'm giving a partial answer. My suggestion: Implement a service method that does nothing and invoke it 10M times in a loop. Do resources leak? If yes, you have your answer.
  3. No, Server and client are independent. The server can be sync, the client async or vice versa. Both cannot even tell the difference of what the other does. The two services are separated by TCP and a well-defined protocol. It is impossible for a client to even know what the server does. The server does not even have to use .NET.
  4. I'm not sure what you're asking. Under the hood, WCF clients use TCP. Incoming data will be handled "somewhere" (in practice on the thread-pool).
  5. If your code is fundamentally synchronous, this is the best you can do. You'll burn one thread waiting for N asynchronous service calls. That's ok.
  6. Why don't you just specify a callback in BeginXXX that does nothing else but call EndXXX? That way you always call EndXXX and conform to how the framework is meant to be used. You can still use wait handles.
usr
  • 168,620
  • 35
  • 240
  • 369
  • 1
    Your point 6 is a brilliant idea! Why did I not think of it myself? ;o) I let a callback do all EndXXX calls but signal the first one with ManualResetEvent to the original thread. Presumably I cannot use both callback and handle from the asyncresult. I don't know which one is being called/signaled first? – aku Mar 22 '14 at 10:05
  • You don't care which one is signaled first. If you do care about it, though, you can create an event manually and signal it inside of the callback. Async IO is normally not used with evens because the whole point is *not* to wait. Your case is an exception because you only wait once for multiple async IOs. – usr Mar 22 '14 at 10:15
  • In my case the client logic above is synchronous so the idea is to convert a sync call to async calls to multiple servers and use the fastest result. Thanks for your ideas, I have the point 6 with callbacks up and running nicely. Also simple and easy to understand what is going on. – aku Mar 26 '14 at 20:44
  • @aku great. You could convert the APM pattern to the TAP pattern by using Task.Factory.FromAsync. Then you can say `Task.WaitAny` to get the quickest task. This automates the EndXXX stuff and makes the code maximally clean. – usr Mar 26 '14 at 20:55
2

Depends on the object you call the begin/end pattern on. some are known to leak. from CLR via C# by Jeffrey Richter:

You must call Endxxx or you will leak resources. CLR allocates some internal resources when you initiate asynchronous operation. If Endxxx is never called, these resources will be reclaimed only when the process terminates.

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
  • I have CLR via C#, 4th Edition as a PDF. I cannot find any parts of your quote in it. Can you specify your source, i.e. edition and page? – aku Mar 22 '14 at 09:55
  • Looks to me that Richter's CLR via C#, 4th Edition has replaced the chapter on the original async pattern with the new Task async/await stuff. – aku Mar 26 '14 at 21:14
  • Sorry, didn't notice the first comment. It's in 2nd edition p 615 (section **Important Notes about the APM**). Maybe the 3rd as well, I don't know. – Peter Ritchie Mar 26 '14 at 21:20
1

AFAIK the Task-based pattern uses the thread pool to handle its work. My client makes thousands of calls per second and would completely trash the thread pool.

It would be so if you used Task.Run or Task.Factory.StartNew. By itself, Task.Factory.FromAsync doesn't create or switch threads explicitly.

Back to your scenatio:

I want to use only the reply from the service that answers faster, i.e. when WaitHandle.WaitAny returns with an index I only call EndXXX with the corresponding IAsyncResult to get the faster result. I don't ever call the other EndXXX.

Let's create the Task wrapper for BeginXXX/EndXXX asynchronous service call:

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);
    }
}

And implement the whatever-service-answers-faster logic:

static async Task<object> CallBothServicesAsync(
    IService service1, IService service2, object arg)
{
    var task1 = service1.WorkAsync(arg);
    var task2 = service2.WorkAsync(arg);

    var resultTask = await Task.WhenAny(task1, task2).ConfigureAwait(false);
    return resultTask.Result;
}

So far, there has been no blocking code and we still don't create new threads explicitly. The WorkAsync wrapper passes a continuation callback to BeginWork. This callback will be called by the service when the operation started by BeginWork has finished.

It will be called on whatever thread happened to serve the completion of such operation. Most often, this is a random IOCP (input/output completion port) thread from the thread pool. For more details, check Stephen Cleary's "There Is No Thread". The completion callback will automatically call EndWork to finalize the operation and retrieve its result, so the service won't leak resources, and store the result inside the Task<object> instance (returned by WorkAsync).

Then, your code after await Task.WhenAny will continue executing on that particular thread. So, there may be a thread switch after await, but it naturally uses the IOCP thread where the asynchronous operation has completed.

You almost never need to use low-level synchronization primitives like manual reset events with Task Parallel Library. E.g., if you need to wait on the result of CallBothServicesAsync, you'd simple do:

var result = CallBothServicesAsync(service1, service2).Result;
Console.WriteLine("Result: " + result);

Which is the same as:

var task = CallBothServicesAsync(service1, service2);
task.Wait();
Console.WriteLine("Result: " + task.result);

This code would block the current thread, similarly to what WaitHandle.WaitAny does in your original scenario.

Now, blocking like this is not recommended either, as you'd loose the advantage of the asynchronous programming model and hurt the scalability of your app. The blocked thread could be doing some other useful work rather than waiting, e.g., in case with a web app, it could be serving another incoming client-side request.

Ideally, your logic should be "async all the way", up to some root entry point. E.g., with a console app:

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);
    }
}
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    Very interesting ideas. I already have usr's idea 6 (async callback calls the EndXXX) working and the code is simple and easy to understand but I will try yours when I get the time. It looks very elegant but maybe not so easy to understand than usr's idea 6. My client logic above the distributed async calls is synchronous so I need to wait to get the fastest result. I agree that the client would probably perform better asynchronously all the way but I don't have the option of rewriting it all. – aku Mar 26 '14 at 20:27
  • The idea is to convert a sync call to async calls to multiple servers and use the fastest result. – aku Mar 26 '14 at 20:37
  • @aku, right. You'd avoid blocking waits, your service will scale better. Plus, `async/await` will greatly simply the structure of your code, including exception handling. – noseratio Mar 26 '14 at 20:43
  • I have a further concern regarding communication exceptions which I sometimes get from my EndXXX calls because the channel is in a faulted state. Does the framework clean-up resources in that case because I did not get EndXXX call through? Is there something I should do in this case? – aku Mar 26 '14 at 20:56
  • Still another question about faulted channels within EndXXX calls: can I make the EndXXX call via another channel than the BeginXXX call? My client has several channels open to each service. – aku Mar 26 '14 at 21:01
  • @aku, all I can say is that you really *have* to call `EndXXX` on the *same* `IAsyncResult` returned by the matching `BeginXXX`. `Task.Factory.FromAsync` would do that for you automatically. – noseratio Mar 26 '14 at 21:04