Groups | Blog | Home
all groups > dotnet framework > november 2006 >

dotnet framework : Garbage collection and async operations


Bob Altman
11/30/2006 10:49:33 AM
Suppose I have the following code:

' Get a new instance of some class
Dim myWorker As New MyWorker

' Start a long-running operation on a background thread.
dim deleg as new SomeMethodDeleg(AddressOf myWorker.SomeMethod)
deleg.BeginInvoke(...)

' Abandon the object instance
myWorker = Nothing

At this point I would expect my MyWorker object to be eligible for garbage
collection. But it's still busy running code on a background thread. What
happens when the GC runs?

Jon Shemitz
11/30/2006 12:04:48 PM
[quoted text, click to view]

Your expectation is wrong. The ThreadPool thread has a reference to
your delegate (it has to, to Invoke it) and so the delegate will
survive until you call EndInvoke.

--

..NET 2.0 for Delphi Programmers
www.midnightbeach.com/.net
Bob Altman
11/30/2006 12:48:55 PM
I'm not sure I understand the ramifications of your answer. Let me change
the question slightly and see what shakes out. Suppose I have a class that
provides its own BeginXxx/EndXxx implementation:

Class Test
Public Sub DoWork()
' <Perform some long-running operation>
End Sub

Private _deleg As New TestDelegate(AddressOf DoWork)

Public Function BeginDoWork( _
ByVal callback As AsyncCallback, ByVal state As Object) As IAsyncResult
Return _deleg.BeginInvoke(callback, state)
End Function

Public Sub EndDoWork(asyncResult As IAsyncResult)
_deleg.EndInvoke(asyncResult)
End Sub
End Class

Now, my main program does the following:

Dim test As New Test
test.BeginDoWork(Nothing, Nothing)
test = Nothing

Does something keep the (now abandoned) Test instance alive forever (since
I'm never calling the delegate's EndInvoke)?

Jon Shemitz
11/30/2006 1:46:40 PM
[quoted text, click to view]

Actually, I was in a bit of a rush to get out the door to an
appointment, and I misspoke, slightly.

The ThreadPool thread that's executing your delegate obviously has to
have a reference to the delegate that it's executing, so clearly the
delegate can't be collected until it returns and the ThreadPool thread
is returned to the pool. (Presumably waiting threads have their
delegate-to-execute field cleared, precisely so that the delegates can
be collected.)

You should always call EndInvoke when you execute a delegate
asynchronously. The documentation is very firm about that, even if
it's not clear precisely why. Fwiw, I believe William Sullivan is
wrong about not calling EndInvoke causing a memory leak: the thread is
presumably returned to the pool as soon as the delegate returns, and
if you simply ignore the IAsyncResult that BeginInvoke returns, it
will ultimately be finalized and collected.

Take away messages:

1) Abandoning your MyWorker delegate will not cause premature
collection.

2) Not calling EndInvoke will not force the delegate to be immortal.

3) You should always call EndInvoke when you BeginInvoke a delegate.
If you don't want to bother, use ThreadPool.QueueUserWorkItem instead
of asynch execution.

--

..NET 2.0 for Delphi Programmers
www.midnightbeach.com/.net
AddThis Social Bookmark Button