-
Mar 22nd, 2023, 09:32 AM
#1
Thread Starter
Member
[RESOLVED] Disposing Multiple Instantiated Objects (Classes) With Long Run-times
At run-time, my Winform app starts (instantiates) multiple classes which are all IDisposable. Each class spawns a threaded method which often has long run times (several minutes).
If a run only requires use of one class, then naturally I won't thread the method in that class and therefore can dispose the class immediately thereafter. The choice of threading the method or not is set by use of a boolean, for example, called threadAllMethods.
Code:
threadAllMethods = false
Dim MyMath1 as New MathClass1(param1, param2, param3)
MyMath1.Dispose()
If a run requires use of all classes, then I will thread the method in each class, so that use of multiple threads can run simultaneously.
Code:
threadAllMethods = true
Dim MyMath1 as New MathClass1(param1, param2, param3)
Dim MyMath2 as New MathClass2(param1, param2, param3)
Dim MyMath3 as New MathClass3(param1, param2, param3)
Dim MyMath4 as New MathClass4(param1, param2, param3)
Dim MyMath5 as New MathClass5(param1, param2, param3)
However, when all classes are instantiated and their methods are threaded, you cannot invoke Dispose immediately thereafter since you need to wait until the threads are completed. Therefore, I define a public list of Object type as follows:
Code:
Public ListOfClasses As New List(Of Object)
Next, after each class is instantiated with method threading, I add the object to the public list of objects:
Code:
threadAllMethods = true
Dim MyMath1 as New MathClass1(param1, param2, param3)
ListOfClasses.Add(MyMath1)
Dim MyMath2 as New MathClass2(param1, param2, param3)
ListOfClasses.Add(MyMath2)
Dim MyMath3 as New MathClass3(param1, param2, param3)
ListOfClasses.Add(MyMath3)
Dim MyMath4 as New MathClass4(param1, param2, param3)
ListOfClasses.Add(MyMath4)
Dim MyMath5 as New MathClass5(param1, param2, param3)
ListOfClasses.Add(MyMath5)
Then, by using AutoResetEvents with e.g. MyMath1ThreadDone.Set specified at the end of a threaded method, I wait for all threads (methods) to complete, and then dispose the IDisposable classes using the a Do While:
Code:
MyMath1ThreadDone.WaitOne()
MyMath2ThreadDone.WaitOne()
MyMath3ThreadDone.WaitOne()
MyMath4ThreadDone.WaitOne()
MyMath5ThreadDone.WaitOne()
While (ListOfClasses.Count() > 0)
Dim item As Object = ListOfClasses(0)
ListOfClasses.RemoveAt(0)
item.Dispose
End While
FYI, the Dispose code I am using was fetched from MSDN a while ago, and I am not sure that it guarantees proper finalization:
Code:
#Region "IDisposable"
'---Code entirely copied from MSDN
Private managedResource As System.ComponentModel.Component
Private unmanagedResource As IntPtr
Protected disposed As Boolean = False
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposed Then
If disposing Then
Try
' managedResource.Dispose()
Catch
End Try
End If
' Add code here to release the unmanaged resource.
unmanagedResource = IntPtr.Zero
' Note that this is not thread safe.
End If
Me.disposed = True
End Sub
'Do not change or add Overridable to these methods.
'Put cleanup code in Dispose(ByVal disposing As Boolean).
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overrides Sub Finalize()
Dispose(False)
MyBase.Finalize()
End Sub
#End Region
Some questions I have are (a) When the methods inside all classes are to be threaded, is it appropriate to load the classes as objects into a list and then wait for all threads to stop and then dispose the classes, and (b) based on the MSDN dispose code I am using, will it finalize correctly, and will it release unmanaged resources as well? (As far as manually disposing GDIObjects, I just spent a lot of time making sure all the Pen, Brush, and bitmaps are individually disposed within their respective methods -- since GDIObjects tend to fill up Byte[] arrays that are unmanaged without handles and are not disposed via IDisposable).
Last, instead of loading all the classes into an object list, and then waiting for all their threaded methods to complete and then disposing classes en-block, can I use the following code at the end of a threaded method to invoke disposal of its parent class, as follows?
Code:
Sub DoTheMath(ByVal param1 As Double, ByVal param2 As Double, ByVal param3 As Double)
'....do some work
MyMath1ThreadDone.Set() 'always used/needed for thread mgmt
Me.Dispose
End Sub
Last edited by pel11; Mar 30th, 2023 at 11:29 AM.
-
Mar 22nd, 2023, 10:43 AM
#2
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
Without knowing more this is just a guess. First I'd design my classes like this taking advantage of inheritance,
Code:
Public MustInherit Class MathClass
Implements IDisposable
Protected p1 As Integer
Protected p2 As Integer
Protected p3 As Integer
Public answer As Integer
Public MathTask As Task
Public Sub New(param1 As Integer, param2 As Integer, param3 As Integer)
Me.p1 = param1
Me.p2 = param2
Me.p3 = param3
Me.MathTask = Task.Run(Sub()
Me.DoTheMath()
End Sub)
End Sub
Public Sub New()
Throw New NotImplementedException
End Sub
Public MustOverride Sub DoTheMath()
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Public Class MathClass1
Inherits MathClass
Public Sub New(param1 As Integer, param2 As Integer, param3 As Integer)
MyBase.New(param1, param2, param3)
End Sub
Public Sub New()
MyBase.New()
End Sub
Public Overrides Sub DoTheMath()
Me.answer = Me.p1 + Me.p2 + Me.p3
End Sub
End Class
Public Class MathClass2
Inherits MathClass
Public Sub New(param1 As Integer, param2 As Integer, param3 As Integer)
MyBase.New(param1, param2, param3)
End Sub
Public Sub New()
MyBase.New()
End Sub
Public Overrides Sub DoTheMath()
Me.answer = Me.p1 - Me.p2 - Me.p3
End Sub
End Class
Public Class MathClass3
Inherits MathClass
Public Sub New(param1 As Integer, param2 As Integer, param3 As Integer)
MyBase.New(param1, param2, param3)
End Sub
Public Sub New()
MyBase.New()
End Sub
Public Overrides Sub DoTheMath()
Me.answer = Me.p1 * Me.p2 * Me.p3
End Sub
End Class
Then I'd do the same thing regardless of how many math objects,
Code:
Public MathObjs As New List(Of MathClass)
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'ONE OBJECT
MathObjs.Clear()
Dim MyMath1 As New MathClass1(3, 5, 7)
MathObjs.Add(MyMath1)
Await Task.Run(Sub()
Task.WaitAll((From m In MathObjs Select m.MathTask).ToArray)
End Sub)
MyMath1.Dispose()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'MULTIPLE OBJECTS
MathObjs.Clear()
For x As Integer = 1 To 3
Dim MyMath1 As New MathClass1(x, x + 1, x + 2)
Dim MyMath2 As New MathClass2(x, x + 1, x + 2)
Dim MyMath3 As New MathClass3(x, x + 1, x + 2)
MathObjs.Add(MyMath1)
MathObjs.Add(MyMath2)
MathObjs.Add(MyMath3)
Next
Await Task.Run(Sub()
Task.WaitAll((From m In MathObjs Select m.MathTask).ToArray)
End Sub)
For Each mobj As MathClass In MathObjs
mobj.Dispose()
Next
End Sub
-
Mar 22nd, 2023, 11:57 AM
#3
Thread Starter
Member
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
Thanks for putting this together. I understand your logic and approach. Now specifically, a lot of the parameters are different for each math class - as the use of params 1,2,3 was a sort of "cartoon" (i.e., textbook, academic) example so that the context was KISS.
Since the math and all associated params are different across the classes, I think your use of MathClass and MathTask won't work(?). However, if you had e.g. params 1,2 for class MyMath1, params 3,4,5,6,7 for MyMath2, and e.g. params 8,9,10, for MyMath3 (just to show they are all different params and that each class is very different), is there still a way to inherit a standardized MathClass and MathTask? The challenge is that you have to listen for the methods to complete, and since they're threaded when multiple class runs are involved, I merely use the AutoResetEvent and WaitOne(), which seems to do a good job, but this doesn't guarantee a streamlined disposal method like you showed when assuming all params were the same.
-
Mar 22nd, 2023, 02:54 PM
#4
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
 Originally Posted by pel11
Thanks for putting this together. I understand your logic and approach. Now specifically, a lot of the parameters are different for each math class - as the use of params 1,2,3 was a sort of "cartoon" (i.e., textbook, academic) example so that the context was KISS.
Since the math and all associated params are different across the classes, I think your use of MathClass and MathTask won't work(?). However, if you had e.g. params 1,2 for class MyMath1, params 3,4,5,6,7 for MyMath2, and e.g. params 8,9,10, for MyMath3 (just to show they are all different params and that each class is very different), is there still a way to inherit a standardized MathClass and MathTask? The challenge is that you have to listen for the methods to complete, and since they're threaded when multiple class runs are involved, I merely use the AutoResetEvent and WaitOne(), which seems to do a good job, but this doesn't guarantee a streamlined disposal method like you showed when assuming all params were the same.
Maybe you could supply the signatures for the constructors for all of the classes.
-
Mar 26th, 2023, 09:33 PM
#5
Thread Starter
Member
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
Thanks, but there's too many classes and too many different parameters. Thus, my earlier reply with "...params 1,2 for class MyMath1, params 3,4,5,6,7 for MyMath2, and e.g. params 8,9,10, for MyMath3" would work for your template. You don't need an explicit list of exactly what the param names are or what the explicit class names are, since they don't matter. As long as non-overlapping params are used for 3 classes, that should suffice for your signatures.
-
Mar 27th, 2023, 09:13 AM
#6
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
 Originally Posted by pel11
Thanks, but there's too many classes and too many different parameters. Thus, my earlier reply with "...params 1,2 for class MyMath1, params 3,4,5,6,7 for MyMath2, and e.g. params 8,9,10, for MyMath3" would work for your template. You don't need an explicit list of exactly what the param names are or what the explicit class names are, since they don't matter. As long as non-overlapping params are used for 3 classes, that should suffice for your signatures.
Not sure what your point is but I took a stab and made a few changes,
The classes
Code:
Public MustInherit Class MathClass
Implements IDisposable
Protected pA As Integer?
Protected pB As Integer?
Protected pC As Integer?
Protected pD As Integer?
Protected pE As Integer?
Protected pF As Integer?
Protected pZ As Double?
Public answer As Integer?
Public MathTask As Task
Public Sub New(Optional param1 As Integer? = Nothing,
Optional param2 As Integer? = Nothing,
Optional param3 As Integer? = Nothing,
Optional param4 As Integer? = Nothing,
Optional param5 As Integer? = Nothing,
Optional param6 As Integer? = Nothing,
Optional param26 As Double? = Nothing)
Me.pA = param1
Me.pB = param2
Me.pC = param3
Me.pD = param4
Me.pE = param5
Me.pF = param6
'
'
'
Me.pZ = param26
Me.MathTask = Task.Run(Sub()
Me.DoTheMath()
End Sub)
End Sub
Public Sub New()
Throw New NotImplementedException
End Sub
Public Function didStart() As Boolean
Dim rv As Boolean = False
If Me.MathTask IsNot Nothing Then
rv = True
End If
Return rv
End Function
Public Function isComplete() As Boolean
Dim rv As Boolean = False
If Me.didStart AndAlso
(Me.MathTask.Status = TaskStatus.RanToCompletion OrElse
Me.MathTask.Status = TaskStatus.Faulted) Then
rv = True
End If
Return rv
End Function
Public MustOverride Sub DoTheMath()
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Public Class AddClass
Inherits MathClass
Public Sub New(param1 As Integer, param2 As Integer)
MyBase.New(param1, param2)
End Sub
Public Overrides Sub DoTheMath()
Me.answer = Me.pA + Me.pB
End Sub
End Class
Public Class SubtractClass
Inherits MathClass
Public Sub New(num1 As Integer, num2 As Integer, num3 As Integer)
MyBase.New(num1, num2, num3)
End Sub
Public Overrides Sub DoTheMath()
Me.answer = Me.pA - Me.pB - Me.pC
End Sub
End Class
Public Class MultiplyClass
Inherits MathClass
Public Sub New(foo1 As Integer, foo2 As Integer, foo3 As Integer, foo4 As Integer)
MyBase.New(, , foo1, foo2, foo3, foo4)
End Sub
Public Overrides Sub DoTheMath()
Me.answer = Me.pC * Me.pD * Me.pE * Me.pF
End Sub
End Class
Public Class SqrtClass
Inherits MathClass
Public Sub New(ADbl As Double)
MyBase.New(, , , , , , ADbl)
End Sub
Public Overrides Sub DoTheMath()
Me.answer = CType(Math.Sqrt(CDbl(Me.pZ)), Integer?)
End Sub
End Class
Test
Code:
Public MathObjs As New List(Of MathClass)
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'ONE OBJECT
MathObjs.Clear()
Dim MyMath1 As New SubtractClass(3, 5, 7)
MathObjs.Add(MyMath1)
Await Task.Run(Sub()
Task.WaitAll((From m In MathObjs Select m.MathTask).ToArray)
End Sub)
MyMath1.Dispose()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'MULTIPLE OBJECTS
MathObjs.Clear()
For x As Integer = 1 To 3
Dim MyMath1 As New AddClass(x, x + 1)
Dim MyMath2 As New SubtractClass(x, x + 1, x + 2)
Dim MyMath3 As New MultiplyClass(x, x + 1, x + 2, x + 4)
Dim MyMath4 As New SqrtClass(x + 1)
MathObjs.Add(MyMath1)
MathObjs.Add(MyMath2)
MathObjs.Add(MyMath3)
MathObjs.Add(MyMath4)
Next
Await Task.Run(Sub()
Task.WaitAll((From m In MathObjs Select m.MathTask).ToArray)
End Sub)
For Each mobj As MathClass In MathObjs
'If mobj.isComplete Then
' ' Stop
'End If
mobj.Dispose()
Next
End Sub
-
Mar 27th, 2023, 11:15 AM
#7
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
 Originally Posted by pel11
Some questions I have are (a) When the methods inside all classes are to be threaded, is it appropriate to load the classes as objects into a list and then wait for all threads to stop and then dispose the classes, and (b) based on the MSDN dispose code I am using, will it finalize correctly, and will it release unmanaged resources as well? (As far as manually disposing GDIObjects, I just spent a lot of time making sure all the Pen, Brush, and bitmaps are individually disposed within their respective methods -- since GDIObjects tend to fill up Byte[] arrays that are unmanaged without handles and are not disposed via IDisposable).
Are there any class level objects that need disposing then? If each method that uses a disposable object is disposing of them, as you said it is, then you wouldn't need to make the class disposable anyway. You would only really need to be disposable if there are class level disposable members.
If you don't want to wait till all objects have completed, you could possibly use a callback or raise an event on completion to let the calling code know a given instance has finished, you could then dispose of the instance as soon as it is finished.
-
Mar 29th, 2023, 12:23 PM
#8
Thread Starter
Member
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
@dbasnett - Thanks - I do like the standardization of code regarding all the parameters. Using this approach I could first document (write down) a unique list of parameters (many are re-used), select the order I want, and then complete the coding. This would ensure a high level of order of what's happening in the code.
-
Mar 29th, 2023, 12:29 PM
#9
Thread Starter
Member
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
@PlausiblyDamp - I am concerned about the classes being disposed. Each class constructor (i.e., Sub New(,,,)) starts a major threaded method that does a lot of math, withs call to other methods in the same class. So when the thread has completed -- which I detect using an AutoResetEvent - I no longer need the class (or the other methods it called) and then believe I should Finalize the class and then let GC do it's thing(?) I have not used Await or callback much, and have not incorporated such. But if there is a simple way to instantiate a class, wait for a callback, and then dispose (finalize), that would be of some help. As I said, at present, I wait for the AutoResetEvent at the end of the threaded (major) method (Thread1IsDone.Set) to trigger, then I dispose the class.
Last edited by pel11; Mar 29th, 2023 at 12:35 PM.
-
Mar 29th, 2023, 12:34 PM
#10
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
 Originally Posted by pel11
@PlausiblyDamp - I am concerned about the classes being disposed. Each class constructor (i.e., Sub New(,,,)) starts a major threaded method that does a lot of math, withs call to other methods in the same class. So when the thread has completed -- which I detect using an AutoResetEvent - I no longer need the class (or the other methods it called) and then believe I should Finalize the class and then let GC do it's thing(?)
It all depends on exactly what kinds of resources the class creates and uses. You say the class is doing a lot of Math and calling methods in the same class, however you aren't making it clear if the class itself is using unmanaged resources (i.e. things that need disposing of). Are there any class level fields that require disposing?
edit: Could you post an example of one of your existing Dispose methods? Just to give us an idea of what clean-up you are doing.
Last edited by PlausiblyDamp; Mar 29th, 2023 at 12:38 PM.
-
Mar 29th, 2023, 12:38 PM
#11
Thread Starter
Member
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
@PlausiblyDamp - There are just a few arrays and lists that are defined at the class level. None of the classes have properties. But the major math method has a lot of arrays and variables that are defined and used. My understanding a long time ago was that if you instantiate a class (which obviously has methods) and the work is done for all methods in the class, it will eventually get disposed by GC. Is that what you are saying?
-
Mar 29th, 2023, 12:41 PM
#12
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
 Originally Posted by pel11
@PlausiblyDamp - There are just a few arrays and lists that are defined at the class level. None of the classes have properties. But the major math method has a lot of arrays and variables that are defined and used. My understanding a long time ago was that if you instantiate a class (which obviously has methods) and the work is done for all methods in the class, it will eventually get disposed by GC. Is that what you are saying?
It all depends on the underlying types in use - if the arrays involve objects that need disposing or the variables are a type that needs disposing then you will need to dispose of them, if they don't then you can ignore them.
If possible could you post an example of one of your Dispose methods? If it isn't obvious from the code could you indicate the data types the Dispose method refers to?
Last edited by PlausiblyDamp; Mar 29th, 2023 at 02:11 PM.
-
Mar 29th, 2023, 03:48 PM
#13
Thread Starter
Member
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
@PlausiblyDamp - the dispose method for all classes is provided in the OP. The methods typically contain mostly double[,], byte[], integer[], and sometimes dictionaries, lists - and I don't think these need to be disposed, as they'd be trashed via GC once the class is disposed. Some of the minor methods in a class perform GDI graphics, but I manually dispose all of their GDIObjects (fonts, rectangle, brushes, pens, bitmaps, and graphics) after each use.
-
Mar 29th, 2023, 05:14 PM
#14
Re: Disposing Multiple Instantiated Objects (Classes) With Long Run-times
 Originally Posted by pel11
@PlausiblyDamp - the dispose method for all classes is provided in the OP. The methods typically contain mostly double[,], byte[], integer[], and sometimes dictionaries, lists - and I don't think these need to be disposed, as they'd be trashed via GC once the class is disposed. Some of the minor methods in a class perform GDI graphics, but I manually dispose all of their GDIObjects (fonts, rectangle, brushes, pens, bitmaps, and graphics) after each use.
If that is the case then it sounds like your classes don't really need to implement IDisposable or provide any Dispose methods. Arrays, doubles, bytes etc. don't need to be disposed of as they are just managed resources and therefore the Garbage Collector will deal with them. GDI Objects do need to be disposed of, but if you are making sure than any method that creates them also disposes of them then there is nothing to Dispose of at the class level either.
From the sound of it you can get rid of all IDisposable code - the implements statement both Dispose Methods and the Finalizer as well.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|