3

I'm setting up a WCF client and server scenario that needs to be reliable and scalable and as part of that I'm trying to make it multithreaded. Admittedly I don't have extensive multithreading experience.

On the client, the callback from the server (in this case "OnMessage") fires a client-side event that should be passed off in the background. I have the client's CallbackBehavior ConcurrencyMode set to Single (at least, for now). So in order to do a reply to the callback, rather than calling the handler as normal (either handler(sender, eventargs, or handler.Invoke...) I'm calling handler.BeginInvoke.

Everything works fine, but in the callback I'm wondering if I need to explicitly call EndInvoke or if I can basically do nothing (here's my lack of multithreading experience showing).

public void OnMessage(Message message)
{
    EventHandler<MessageEventArgs> handler = OnServerMessage;
    if (handler != null)
        handler.BeginInvoke(this, new MessageEventArgs(message), CompleteHandler, handler);
}

public void CompleteHandler(IAsyncResult result)
{
    ((EventHandler<MessageEventArgs>)result.AsyncState).EndInvoke(result);
}

Can I replace CompleteHandler with a direct reference to handler.EndInvoke, or an empty method, or perhaps is there something more I should be doing?

Also, at the moment, the event subscriber blocks waiting on user input from the console. When the application is complete, it would either block waiting on the database, or be asynchronous bottom-to-top (so this may not be necessary, but I'd still like to know).

3 Answers3

8

Yes you have to. Only way to find out if the invoked method threw an exception. And to cleanup the remoting state of the call so it can be garbage collected instead of letting it linger for another 10 minutes.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks for the info. This is one of those cases where I'm finding out just how much I don't know. What do I need to do to 'cleanup the remoting state'? If you can suggest any articles/documentation/samples to read and learn from I'd appreciate that as well. Thanks again. –  Nov 15 '10 at 17:59
  • 3
    To cleanup remoting state, you just need to call `EndInvoke` :) – Anton Tykhyy Nov 15 '10 at 18:03
  • You answer implies that it would linger for a time period, and then be eligible for garbage collection anyway. Are you sure about that? – sisve Nov 15 '10 at 19:15
  • @Simon - great topic for a new question! – Hans Passant Nov 15 '10 at 19:31
  • Per microsoft, you do not *have* to call EndInvoke. Quote: "You can call EndInvoke to retrieve the return value from the delegate, if neccesary, but this is not required. EndInvoke will block until the return value can be retrieved." from: https://msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.90).aspx – Abacus Mar 27 '15 at 20:56
  • That has nothing whatsoever to do with a delegate's BeginInvoke method. The Control class is not a delegate type. [Read this](http://stackoverflow.com/a/15013968/17034). – Hans Passant Mar 27 '15 at 21:00
1

You need to wrap your CompleteHandler code in a try/catch block and then properly handle any exception thrown by the EndInvoke. Right now the service works under 'lab' conditions, but when things go wrong you won't even know what goes wrong, because you don't handle/log errors properly.

As for the issue of getting the state from the global state, or from the IAsyncResult, or from a callback closure: IASyncResult.AsyncState is the most generic solution and will continue to work correctly after somebody else refactors the calling code beyond any recognition.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • In regards to your second paragraph: Sorry, but I don't fully understand. Are you referring to how I am accessing the handler to call EndInvoke? If so, then are you meaning to suggest the method of passing it in as the state object and retrieving it in the callback from the IAsyncResult.AsyncState to call EndInvoke is proper or improper? Thank you. –  Nov 15 '10 at 18:25
  • @Chris: I think I misunderstood the question. I though you are asking if there is another way to get the reference to `handler` inside `CompleteHandler`. – Remus Rusanu Nov 15 '10 at 19:02
0

In general, BeginInvoke starts an asynchronous operation, whereas EndInvoke waits for it to finish. So it depends on your intention: if you want to guarantee that your asynchronous code is finished, you need EndInvoke, otherwise no.

(Beware however that if your BeginInvoke calls into the same thread where you actually are (it's a valid usage, just the code will be run on the next Dispatcher loop), you must not use EndInvoke, as this would create a deadlock. But this seems to be not your case.)

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • Thank you. However, I don't know what calling into the same thread involves in the first place, so I also don't know how to avoid/control it in the future. Do you have an example of how it would differ from my current implementation? –  Nov 15 '10 at 18:18