Question Starting a new thread closes a serialPort

Thibow

Member
Joined
Jul 20, 2012
Messages
9
Programming Experience
Beginner
Hello,
I am currently discovering the wonderful world of multithreading and I am having trouble. On my Main class I am using a SerialPort, handling SerialPort1_DataReceived to read received data. Everything worked ok until I tried to send a heavy function in a new thread to be able to show a nice spinner image on the UI (main form) while executing this function. However, this heavy function uses the result of data received through the serialPort and this is where it gets tricky:

Here is how a sample code of what I am doing:

In my main form, I open the Serialport when clicking on "connect" button
 Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
        Try
            'Get the port name from the application list box.
            SerialPort1.PortName = lstCOMPorts.Items(lstCOMPorts.SelectedIndex).ToString()

            'Open the COM port.
            SerialPort1.Open()[INDENT]Catch ex As Exception
[/INDENT]            btnClose_Click(Me, e)

        End Try
    End Sub


In the main form, when I click on another button I show the spinner and start my new thread with my heavy function

Private Sub BtGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtGo.Click
        BtGo.Enabled = False
        Dim Export1 As New Export(RadioGPX.Checked)
        Dim thread1 As New System.Threading.Thread(AddressOf Export1.Go)
        thread1.SetApartmentState(Threading.ApartmentState.STA)
        AddHandler Export1.FinishedExporting, AddressOf FinishedExportingEventHandler

        setSpinnerVisible(True) 'show the spinner

        MsgBox(Me.SerialPort1.IsOpen.ToString) 'Verify if the port is open --> True

        thread1.Start()

    End Sub


and then finally in Export.vb, the class with my heavy function:
 Public Sub Go()
        MsgBox(Main.SerialPort1.IsOpen.ToString) 'check is the serial port is open ---> FALSE
'heavy function...
End Sub



As you can see, I have several msgBox to tell me if the serialPort is Open. Before starting the thread it is open, and right after starting the new thread, the serialPort is closed ! This results that I can't handle data received by the serialPort while being in my new thread.

Do you know why launching a new thread closes my serialPort conection ?
Thank you very much in advance
 
Last edited:
You are accessing the default form instance, and that will return a new instance in each thread it is accessed. So you're not talking to your original form object as you thought but a completely new object, where its serialport has never been opened. If you that serial port in your procesesing class you should pass it to the class instance, or perhaps better create and use it in the processing class directly.
 
Hi John,
Thank you very much for your help, I think I understand what you meant, however, in fact, I do not need to access directly the serialPort but execute a function that sends a string through the serialPort.
Here is how it goes in my "Export class" that the new thread1 is starting:
Public Sub Go()
Main.sendMessageA(Nothing)
        'then handle main._messageStringReceived that has been updated[/COLOR][/FONT]
'heavy function...
End Sub


my sendMessage() function in the main form is simply :
    Public Sub sendMessageA(ByVal e As System.EventArgs)        Try
            'Write the data to the open serial port
            SerialPort1.Write("a")
        Catch ex As Exception
            'If there was an exception, then close the handle to 
            '  the device and assume that the device was removed
            btnClose_Click(Me, e)
        End Try
    End Sub


So as you can see, I am trying to go through the main form to use the serialPort to send a char "a", this command to my remote device is a request for data, the onDataReceive event would then handle the receiving data and store it in a "
main._messageStringReceived
" that I would consult for the heavy function in the thread1... The major problem is that nothing seems to be sent by the main form.. I guess this is related to the fact that I call the sendMessageA() function from a thread that has no access to the serialPort, however I don't know how to do it.. I have already used "delegate" functions for accessing a textBox on the UI from another thread but I don't know if I can and how to do the same for a function...

Thank you again for the help
 
So as you can see, I am trying to go through the main form to use the serialPort to send a char "a", this command to my remote device is a request for data, the onDataReceive event would then handle the receiving data and store it in a "
main._messageStringReceived
" that I would consult for the heavy function in the thread1...
What you're saying is that you use your thread to send and process received data from the serial port. So why not declare and use the SerialPort in the class you're using it?

As I said it is not possible to use the default form reference from the other thread. If you want access to a form from your thread (for displaying results) you can either (a) pass a reference to that form to the thread, or (b) since you likely only have one such form instance you can also get it from the My.Application.OpenForms collection. You can also turn it around and get rid of the dependency by (c) declaring and raise an event in your worker class and listen to that event in your form class.
 
The fact to open a serial port in the new thread is very interesting however isn't it a bad thing to open and close serial port each time i want to use it ? Because i dont need it for that heavy function only. So what you advise me to do is simply open the serial port in my new thread, process the received data and then close it. And back to my form open it again if I need it ?
I am quite a newby and i don't understand the other solutions you provide could you please provide an example ?
Thank you very much for your help
 
If there is a reason to keep the serialport alive longer than the processing class, then by all means. I don't know your design other than what you've explained.

(b) An example for this code in thread:
Main.sendMessageA(Nothing)
Dim theForm As Main = CType(My.Application.OpenForms("Main"), Main)
theForm.sendMessageA(Nothing)

(a) An example for passing information to a class (dependency):
    Class Export
        Public Property TheForm As Main
        Public Sub Go()
            TheForm.sendMessageA(Nothing)
        End Sub
    End Class

passing the reference to an Export instance:
x.TheForm = Me

(c) An example using declaring and raising event (no dependency)
    Class Export
        Public Event NewInfo(data As String)
        Public Sub Go()
            RaiseEvent NewInfo("hello")
        End Sub
    End Class

handling event in Main:
    Private WithEvents x As Export
    Private Sub x_NewInfo(data As String) Handles x.NewInfo

    End Sub
 
This is awesome, thank you very much, now I get it, I think I will use the raise event solution as it seems to be the nicest. I'll work on this on Monday and be back for a feedback, thank you very much
 
I have used this trick

Dim theForm As Main = CType(My.Application.OpenForms("Main"), Main)
theForm.sendMessageA(Nothing)

(a) An example for passing information to a class (dependency):
    Class Export
        Public Property TheForm As Main
        Public Sub Go()
            TheForm.sendMessageA(Nothing)
        End Sub
    End Class


and it works perfectly well, thank you again for the clear explanation
 
Back
Top