Is Delegate.EndInvoke() really necessary?
Asked Answered
M

7

25

I've read a couple of forums and even a stackoverflow question or two saying that Delegate.EndInvoke is necessary when using Delegate.BeginInvoke. Many of the articles I've read talking about using BeginInvoke have failed to mention using EndInvoke. Also I've deployed production code using only BeginInvoke and there doesn't appear to be any memory issues. The way I've used BeginInvoke is generally with threads that I don't care about when they finish or how long they take to process.

Metagenesis answered 3/1, 2011 at 14:1 Comment(3)
You'll 'leak' memory for 10 minutes, the default remoting lifetime time-out. Yes, not often noticeable. Use ThreadPool.QUWI for fire-and-forget code.Boyles
But everything I've been told says that BeginInvoke uses the threadpool...Metagenesis
Just because it uses the threadpool doesn't mean that it's literally identical to calling QUWI. You could design a class that fires a method asynchronously and then saves a 2 GB file to the hard disk; this would "use the threadpool" but would not be the same as just calling the method with QUWI (without saving the file).Evanne
E
46

From the MSDN article 'Calling Synchronous Methods Asynchronously':

No matter which technique you use, always call EndInvoke to complete your asynchronous call.

Now, there is theory and then there is practice. You have found, like many other developers before you, that you can often get away with ignoring this documented requirement. It may be an implementation detail whether EndInvoke actually does anything that's absolutely necessary to prevent your application from crashing, leaking memory, etc. But here's the thing: if it's a documented requirement, you really ought to do it. This is not just about theory; it's about protecting yourself in the event of change.

By documenting this requirement, the designers of this asynchronous calling mechanism basically gave themselves the freedom to change the way BeginInvoke and EndInvoke work down the line so that, if there were sufficient reason (e.g., a performance enhancement), EndInvoke could suddenly become a lot more necessary. Suppose it would suddenly result in a deadlock if you forgot it. They've already covered themselves by saying always call EndInvoke; if your app stops working because you didn't follow this requirement, the onus is on you.

I'm not saying this is necessarily a likely scenario. My point is simply that you shouldn't—or at least I wouldn't—ask "Is this really necessary?" with the mindset of If I can get away with leaving it out, then I will, since it is documented that you should do it.

Evanne answered 3/1, 2011 at 14:21 Comment(2)
Definitely +1 for this answer.Ribbonwood
This is a tough one for me. As you pointed out, in practive it's not a requirement, but to make the application tolerant of changes to the framework it should be used.Metagenesis
L
15

It is absolutely necessary if you plan on throwing exceptions from your thread and expect to catch them properly. If you do not call EndInvoke, the thread that throws the exception will vanish and you won't know anything about it.

To support EndInvoke, provide an AsyncCallback, and in that callback method, be sure to wrap your call to EndInvoke with a try/catch block.

While you could get away with not doing it if you don't care about what happens in the thread, I would argue that it's a very good habit to get into to just call EndInvoke. You never know, a junior developer could some day get in there and change the code in your thread, and throw an exception. Then the updated app gets deployed, and the service calls start coming in.

Lagoon answered 3/1, 2011 at 14:10 Comment(1)
Voted up. Completely forgot about the exception swallowing issue. Nice reminder! I usually through everything directly to the thread pool but exception catching changes eveything.Trevelyan
E
9

I heard sth about memory leak issues that may arises.

By a keyword search, I found a good discussion.

Does not calling EndInvoke *really* cause a memory leak ?

It can but it won't necessarily. Technically there is no such thing as a memory leak in .NET. Eventually the memory will be reclaimed by the GC. The problem is that it might be around a long time. The reason that you should call EndInvoke is because the results of the invocation (even if there is no return value) must be cached by .NET until EndInvoke is called. For example if the invoked code throws an exception then the exception is cached in the invocation data. Until you call EndInvoke it remains in memory. After you call EndInvoke the memory can be released.

Here is the reference,

Another Reference

Euchre answered 3/1, 2011 at 14:12 Comment(2)
Memory leaks are actually quite easy to create in .NET using GCHandle. Besides, the problem is usually not a memory leak; it's a resource leak. And if some kind of logical-open operation (e.g., BeginInvoke) says you should always call its logical-close operation (e.g., EndInvoke), then I think you should.Saar
Open-ended memory leaks, at least as I would define them, can be easily created in managed memory without GCHandles, COM interop, or anything of the sort. An open-ended memory leak will cause memory to be allocated in such a way that (1) it's totally useless, (2) it can't be garbage-collection until some particular event happens, and (3) an unbounded amount of it could be allocated before that. Stuff that will get cleaned up by a full GC isn't a memory leak by any reasonable definition, but stuff which won't get cleaned up can be.Androecium
F
1

MSDN tells, that it is important to call EndInvoke:

Important note No matter which technique you use, always call EndInvoke to complete your asynchronous call.

Ferraro answered 3/1, 2011 at 14:11 Comment(0)
G
1

It has been documented that EndInvoke is not required (no non-managed resources are allocated—assuming you don't wait on the IAsyncResult) when using BeginInvoke to perform action on the GUI thread in a WinForms application.

However this is a specific exception to the general rule: for every BeginOperation there must be a matching EndOperation. As noted on another A here: if the GUI access code can throw, you'll need the EndInvoke to get the exception.

See here for (sort of) official confirmation: http://blogs.msdn.com/b/cbrumme/archive/2003/05/06/51385.aspx#51395 (it's the comment from Chris Brummie 12 May 2003 5:50pm.

Additional: The documentation for Control.BeginInvoke includes this remark:

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.

So it is official: with WinForm's using asynchronous delegate to perform an action on the GUI thread does not require EndInvoke (unless you need the return value or the possible exception, in either case consider using Invoke).

Gamache answered 3/1, 2011 at 14:36 Comment(2)
Control's BeginInvoke/EndInvoke is completely different than Delegate's BeginInvoke/EndInvoke. You don't need to call Control.EndInvoke, but that has nothing to do with asynchronous delegates. You should call Delegate.EndInvoke for each asynchronous delegate, even in WinForms applications.Saar
@StephenCleary: Indeed it is different--Control.BeginInvoke is (as far as I have seen) the sole exception.Gamache
S
1

The way I've used BeginInvoke is generally with threads that I don't care about when they finish or how long they take to process.

Sounds like you should consider using the Thread class instead of asynchronous delegates.

If you care about the results or detecting errors (which both require marshalling the results/error to the originating thread), then you can use asynchronous delegates, and in this case you would need EndInvoke. Better yet, use the Task or Task<TResult> classes.

If you just want to spin off some independent operation, then use the Thread class.

Saar answered 3/1, 2011 at 18:18 Comment(0)
C
1

From the Windows Form documentation on Control.BeginInvoke()

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.

This is the particular case of Windows Form async call on the UI thread and this this doesn't apply to the general case, yet this can help for those in this situation.

Chest answered 14/3, 2016 at 5:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.