I read that as well, which is why I'm a bit confused about the current
status of this.
Frankly, I'm still confused. I appreciate the link Jon provided, but it's
ancient in industry time. I've submitted a comment on the
Control.BeginInvoke() doc page, referencing the blog and pointing out that
the information really needs to be explicit in the docs one way or the
other.
I'm glad to hear that. Have you measured this somehow? Has your
application been long-running?
Nope...I've taken a head-in-the-sand approach. Sorry I can't offer more
confident information, but I've just trusted that it's not a problem. So
far, it hasn't been. I certainly haven't done any explicit
stress-testing, though at least one application uses Control.BeginInvoke()
to do UI updates for network i/o.
Hmm.. Am I reading you correctly? You can call the EndInvoke from
another
thread? (specifically the thread that gets used for the Inovke?). That
would make things significantly easier. I was under the impression you
had
to call the EndInvoke from the thread that issued the BeginInvoke.
Well, yes and no.
I'm aware of no restriction on what thread calls EndXXX. For sure,
there's no such restriction for the usual models, since normally you call
at least one BeginXXX on the UI thread and the EndXXX is called in a
callback that is executing on a thread pool thread. I believe there's no
restriction for Control.EndInvoke() either.
But you can't call Control.EndInvoke() from the code (as opposed to the
thread) that is itself invoked, at least it doesn't seem to me that you
can. Control.EndInvoke() should not complete until the method that was
invoked has returned. So if you call Control.EndInvoke() from within that
method, it should block waiting for your method to complete, which will
never happen because it's blocked on the call to Control.EndInvoke().
I admit, I haven't actually tried to write that code. But the comment for
that blog entry that raised the whole Control.BeginInvoke() question in
the first place described the exact same issue. It does seem reasonable
to me that one should not expect Control.EndInvoke() to return until the
operation has actually completed.
So, sure...you can call Control.EndInvoke() from the UI thread. But not
from the invoked method running on the UI thread, because that would
deadlock.
One idea I was tossing around was to store the IAsyncResults in a list,
and
every time my Receive callback gets called, walk the list and check for
IsCompleted, then call EndInvoke on all the completed results, which
shouldn't unduly burden the IO thread. However, I was concerned that
calling EndInvoke from a (probably) different IO pool thread than the one
that invoked it was not allowed.
I don't think that would be a problem, and I think that doing it that way
would get you most of the way there. Note, however, that it assumes that
there will always be a receive callback occurring after a call to
Control.BeginInvoke().
This would be true most of the time, but at some point you're going to
have the last receive callback that occurs. This will presumably call
BeginInvoke() still, and there will never be another receive callback to
provide the opportunity to call EndInvoke().
Other solutions would be similar, involving a queue processed somewhere.
You could put a timer on the Form, or run a dedicated consumer thread.
These should all be able to process _all_ of the EndInvokes() necessary.
Even more convoluted, you could use the Delegate async methods
BeginInvoke() and EndInvoke(), which _do_ provide a callback (so you can
easily clean those up). You could use the asynchronously invoked delegate
either to call Control.EndInvoke(), or even just to call Control.Invoke()
directly (avoiding the entire Control.Begin/EndInvoke() issue altogether
).
It's not impossible to ensure that Control.EndInvoke() is called, but it
sure as heck is inconvenient.
That's why I've just taken the lazy way
out and assumed that the statements are true and common-sense prevails
here.
Pete