Hi,

I'm trying to program a multithreaded application - it
currently works, but I have two problems. I'm relatively
new to VB.NET (vb6 and C were my things) so I'm kind of
lost here. The code is based on the countdown /
controlinvoker quickstart at GotDotNet.com.

The problems are:

(1) There's a memory leak somewhere. I don't know where,
but it's probably something stupid I've done in the code
below.

(2) How do I set it up to exit gracefully (i.e. where in
the application should I set threadsRunning = FALSE); at
the moment even when in debug mode it crashes when
stopping - I assume because I haven't sewn things up
properly.

The basics of the thread architecture are below. I've
added a second thread to the quickstart code, and I've
added a second ControlInvoker instance. With regard to the
two threads, one updates the screen and the other
communicates with a data comms interface, and passes some
of this data to the serial port. The communications
routine should take priority over the screen updating.

Can anyone cast an expert eye over it and tell me where
I'm being thick? I'd be very grateful.

Thanks


Andy



===========================

' main application class:


Public Class Form1
Inherits System.Windows.Forms.Form


Private thrComms As Thread
Private thrDisplayUpdate As Thread
Private threadsRunning As Boolean
Private controlInvoker As controlInvoker
Private controlInvoker2 As controlInvoker

' ...

Public Sub New()

MyBase.New()

Try
'This call is required by the Windows Form
Designer.
InitializeComponent()
Catch ex As Exception
Stop
End Try
' InitialiseSerialPort()

'Me.CreateGraphics()
'Add any initialization after the
InitializeComponent() call

Me.thrComms = New Thread(New ThreadStart(AddressOf
Me.Thread_232))
Me.thrDisplayUpdate = New Thread(New ThreadStart
(AddressOf Me.Thread_DisplayUpdate))

controlInvoker = New ControlInvoker(Me)
controlInvoker2 = New ControlInvoker(Me)

Me.thrComms.Priority = ThreadPriority.AboveNormal
Me.thrDisplayUpdate.Priority =
ThreadPriority.Normal

Me.thrComms.Start()
Me.thrDisplayUpdate.Start()

Me.threadsRunning = True


End Sub

'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal
disposing As Boolean)
MyBase.Dispose(disposing)
End Sub


' ....

'---------- FIRST THREAD -----------

Public Sub Thread_232()
' Data comms worker thread

Dim rxmsg_counter As Integer
Dim messages As sPCCanMsg

Dim temp_sec_value As Integer

controlInvoker = New ControlInvoker(Me)
'worker thread

Do
msg_list_size = 50
controlInvoker.Invoke(New MethodCallInvoker
(AddressOf interface_read_Wrapper))
' get 50 messages
If (returned_status = HTX_SUCCESS) Then
' do processing
End If

'...
If cycle_count > 50 Then

' reset interface
'Thread.Sleep(1)
controlInvoker.Invoke(New
MethodCallInvoker(AddressOf interface_stop_Wrapper))

'Thread.Sleep(1)
controlInvoker.Invoke(New
MethodCallInvoker(AddressOf interface_start_Wrapper))

cycle_count = 0
End If



Loop While Me.threadsRunning

End Sub

'-------------- SECOND THREAD --------------
Public Sub Thread_display()
controlInvoker2 = New ControlInvoker(Me)
Do
controlInvoker2.Invoke(New MethodCallInvoker(AddressOf
update_display_wrapper))
Loop While Me.threadRunning
End Sub


Private Sub update_display_wrapper(ByVal arguments() As
Object)
'----- Start the Display task
' Gets data from Global Variables, scales data and updates
the screen.

' ......do lots of screen updating....

End Sub


Private Sub interface_start_wrapper(ByVal arguments() As
Object)
canstatus2 = InterfaceStart(1) ' DLL call to start the
data comms interface
End Sub


Private Sub interface_stop_wrapper(ByVal arguments() As
Object)
canstatus2 = InterfaceStop(1)' DLL call to stop the
data comms interface
End Sub

Private Sub interface_read_wrapper(ByVal arguments() As
Object)
canstatus2 = InterfaceReadEx(1, msg_list(0), 5, 50)'
DLL call to read 50 messages from data comms interface
End Sub



End Class

=====================================

'''' CONTROLINVOKER CLASS ''''

Imports System
Imports System.Collections
Imports System.Windows.Forms


Public Delegate Sub MethodCallInvoker(ByVal o() As Object)

' Control.Invoke allows a method to be invoked on the same
thread as the one
' the control was created on. Unlike in the full .NET
Framework, the .NET
' Compact Framework does not support the Control.Invoke
overload for passing an
' array of objects to pass as arguments. This
ControlInvoker class overcomes
' this limitation.


Public Class ControlInvoker

Private Class MethodCall
Public invoker As MethodCallInvoker
Public arguments() As Object


Public Sub New(ByVal invoker As MethodCallInvoker,
ByVal arguments() As Object)
Me.invoker = invoker
Me.arguments = arguments
End Sub 'New
End Class 'MethodCall

Private control As Control
Private argumentInvokeList As New Queue


' The constructor typically takes a form
Public Sub New(ByVal control As Control)
Me.control = control
End Sub 'New


' The delegate wrapping the method and its arguments
' to be called on the same thread as the control.
Public Sub Invoke(ByVal invoker As MethodCallInvoker,
ByVal ParamArray arguments() As Object)

Me.argumentInvokeList.Enqueue(New MethodCall
(invoker, arguments))

control.Invoke(New EventHandler(AddressOf
ControlInvoke))

End Sub 'Invoke


Private Sub ControlInvoke(ByVal sender As Object,
ByVal e As EventArgs)
Dim methodCall As MethodCall = CType
(argumentInvokeList.Dequeue(), MethodCall)
Try
methodCall.invoker(methodCall.arguments)
Catch ex As Exception
Debug.Assert(False)

End Try

End Sub 'ControlInvoke
End Class 'ControlInvoker