Multi-thread haning up main thread, going in order

2020-05-05 17:13发布

问题:

I'm having an issue with multithreading pings that are hanging up my main thread. When debugging the issue, I notice that while the main thread is hung up, it is starting each thread and moving to the next for the pings. Basically it has to ping 5 different IP addresses, and if they're all down my whole thread hangs up for 20 - 30 seconds. I'm using the BeginInvoke, but it still doesn't seem to work right.

Another oddity is that I added a message box at the end of each thread, just to see how they're completing. I have 5 threads and at the end of each is a message box that pops up and says "Done." Well, instead of popping up just 5 times, it's coming up 10 times as if it's running twice. Normally I don't have message boxes in these threads, it is just there for me to try and figure out what is going on, but I'm stumped.

This gets the IP addresses and starts the threads:

Private Sub PingThreadStart()

    Host = zeroStoreNum


    IP = "10."
    Select Case (Host.Substring(0, 1))
        Case "0"
            IP = IP & "10."
        Case "1"
            IP = IP & "11."
        Case "2"
            IP = IP & "12."
        Case "3"
            IP = IP & "13."
        Case "4"
            IP = IP & "14."
        Case "5"
            IP = IP & "15."
        Case "6"
            IP = IP & "16."
        Case "7"
            IP = IP & "17."
        Case "8"
            IP = IP & "18."
        Case "9"
            IP = IP & "19."
    End Select

    Select Case (Host.Substring(1, 1))
        Case "0"
            'IP = IP & "0"
        Case "1"
            IP = IP & "1"
        Case "2"
            IP = IP & "2"
        Case "3"
            IP = IP & "3"
        Case "4"
            IP = IP & "4"
        Case "5"
            IP = IP & "5"
        Case "6"
            IP = IP & "6"
        Case "7"
            IP = IP & "7"
        Case "8"
            IP = IP & "8"
        Case "9"
            IP = IP & "9"
    End Select

    Select Case (Host.Substring(2, 1))
        Case "0"
            IP = IP & "0."
        Case "1"
            IP = IP & "1."
        Case "2"
            IP = IP & "2."
        Case "3"
            IP = IP & "3."
        Case "4"
            IP = IP & "4."
        Case "5"
            IP = IP & "5."
        Case "6"
            IP = IP & "6."
        Case "7"
            IP = IP & "7."
        Case "8"
            IP = IP & "8."
        Case "9"
            IP = IP & "9."
    End Select
    If Host = 100 Then
        IP = "10.10.100."
    End If
    If Host = 200 Then
        IP = "10.11.100."
    End If
    If Host = 300 Then
        IP = "10.12.100."
    End If
    If Host = 400 Then
        IP = "10.13.100."
    End If
    If Host = 500 Then
        IP = "10.14.100."
    End If
    If Host = 600 Then
        IP = "10.15.100."
    End If
    If Host = 700 Then
        IP = "10.16.100."
    End If
    If Host = 800 Then
        IP = "10.17.100."
    End If
    If Host = 900 Then
        IP = "10.18.100."
    End If

    lblIPschemeCH.Text = IP & "X"

    SonicWALL = IP & "1"
    primary = IP & "2"
    secondary = IP & "3"

    Dim PingPublicTry As Thread = New Thread(AddressOf PingPublicTH)
    Dim PingSWpublicTry As Thread = New Thread(AddressOf PingSWpublicTH)
    Dim PingDotOneTry As Thread = New Thread(AddressOf PingDotOneTH)
    Dim PingDotTwoTry As Thread = New Thread(AddressOf PingDotTwoTH)
    Dim PingDotThreeTry As Thread = New Thread(AddressOf PingDotThreeTH)

    PingPublicTry.IsBackground = True
    PingSWpublicTry.IsBackground = True
    PingDotOneTry.IsBackground = True
    PingDotTwoTry.IsBackground = True
    PingDotThreeTry.IsBackground = True


    If ModemPublic = "DHCP" Or SonicWALLPublic = "DHCP" Then
        PingDotOneTry.Start()
        PingDotTwoTry.Start()
        PingDotThreeTry.Start()
    Else
        PingPublicTry.Start()
        PingSWpublicTry.Start()
        PingDotOneTry.Start()
        PingDotTwoTry.Start()
        PingDotThreeTry.Start()
    End If



End Sub

And this is my treads:

    Private Sub PingPublicTH()
    Dim pingactmodem As New System.Net.NetworkInformation.Ping
    Dim pingretmodem As System.Net.NetworkInformation.PingReply
    Dim speedmodem As Integer

    Try
        pingretmodem = pingactmodem.Send(ModemPublic)
        speedmodem = pingretmodem.RoundtripTime
    Catch ex As Exception

    End Try

    If (lblModCh.InvokeRequired) Then

        Dim show As New PingPublicDel(AddressOf PingPublicTH)
        Me.lblModCh.BeginInvoke(show)
    Else
        If speedmodem >= 1 And speedmodem <= 500 Then
            lblModCh.BackColor = Color.Green
        ElseIf speedmodem >= 501 And speedmodem <= 1500 Then
            lblModCh.BackColor = Color.Orange
        ElseIf speedmodem >= 1501 Then
            lblModCh.BackColor = Color.Red
        ElseIf speedmodem = 0 Then
            lblModCh.BackColor = Color.Black

        End If
    End If
    MessageBox.Show("Done modem")


End Sub
Private Sub PingSWpublicTH()
    Dim pingactswp As New System.Net.NetworkInformation.Ping

    Dim pingretswp As System.Net.NetworkInformation.PingReply
    Dim speedswp As Integer

    Try
        pingretswp = pingactswp.Send(SonicWALLPublic)
        speedswp = pingretswp.RoundtripTime
    Catch ex As Exception
    End Try

    If (lbSWPCh.InvokeRequired) Then

        Dim show As New PingSwPublicDel(AddressOf PingSWpublicTH)
        Me.lbSWPCh.BeginInvoke(show)
    Else
        If speedswp >= 1 And speedswp <= 500 Then
            lbSWPCh.BackColor = Color.Green
        ElseIf speedswp >= 501 And speedswp <= 1500 Then
            lbSWPCh.BackColor = Color.Orange
        ElseIf speedswp >= 1501 Then
            lbSWPCh.BackColor = Color.Red
        ElseIf speedswp = 0 Then
            lbSWPCh.BackColor = Color.Black

        End If

    End If
    MessageBox.Show("Done swp")

End Sub
Private Sub PingDotOneTH()
    Dim pingact1 As New System.Net.NetworkInformation.Ping
    Dim pingret1 As System.Net.NetworkInformation.PingReply
    Dim speed1 As Integer



    pingret1 = pingact1.Send(SonicWALL)
    speed1 = pingret1.RoundtripTime

    If (lblSWch.InvokeRequired) Then

        Dim show As New PingDotOneDel(AddressOf PingDotOneTH)
        Me.lblSWch.BeginInvoke(show)

    Else

        If speed1 >= 1 And speed1 <= 500 Then
            lblSWch.Text = (speed1)
            lblSWch.BackColor = Color.Green
        ElseIf speed1 >= 501 And speed1 <= 1500 Then
            lblSWch.Text = (speed1)
            lblSWch.BackColor = Color.Orange
        ElseIf speed1 >= 1501 Then
            lblSWch.Text = (speed1)
            lblSWch.BackColor = Color.Red
        ElseIf speed1 = 0 Then
            lblSWch.Text = "Down"
            lblSWch.BackColor = Color.Black

        End If
    End If
    MessageBox.Show("Done .1")
End Sub
Private Sub PingDotTwoTH()
    Dim pingact2 As New System.Net.NetworkInformation.Ping
    Dim pingret2 As System.Net.NetworkInformation.PingReply
    Dim Speed2 As Integer


    pingret2 = pingact2.Send(primary)
    Speed2 = pingret2.RoundtripTime

    If (lblMainpcCH.InvokeRequired) Then
        Dim show As New PingDotTwoDel(AddressOf PingDotTwoTH)
        Me.lblMainpcCH.BeginInvoke(show)

    Else

        If Speed2 >= 1 And Speed2 <= 500 Then
            lblMainpcCH.Text = (Speed2)
            lblMainpcCH.BackColor = Color.Green
        ElseIf Speed2 >= 501 And Speed2 <= 1500 Then
            lblMainpcCH.Text = (Speed2)
            lblMainpcCH.BackColor = Color.Orange
        ElseIf Speed2 >= 1501 Then
            lblMainpcCH.Text = (Speed2)
            lblMainpcCH.BackColor = Color.Red
        ElseIf Speed2 = 0 Then
            lblMainpcCH.Text = "Down"
            lblMainpcCH.BackColor = Color.Black

        End If

    End If
    MessageBox.Show("Done .2")
End Sub
Private Sub PingDotThreeTH()
    Dim pingact3 As New System.Net.NetworkInformation.Ping
    Dim pingret3 As System.Net.NetworkInformation.PingReply
    Dim speed3 As Integer



    pingret3 = pingact3.Send(secondary)
    speed3 = pingret3.RoundtripTime


    If (lblSecondch.InvokeRequired) Then

        Dim show As New PingDotThreeDel(AddressOf PingDotThreeTH)

        Me.lblSecondch.BeginInvoke(show)

    Else

        If speed3 >= 1 And speed3 <= 500 Then
            lblSecondch.Text = (speed3)
            lblSecondch.BackColor = Color.Green
        ElseIf speed3 >= 501 And speed3 <= 1500 Then
            lblSecondch.Text = (speed3)
            lblSecondch.BackColor = Color.Orange
        ElseIf speed3 >= 1501 Then
            lblSecondch.Text = (speed3)
            lblSecondch.BackColor = Color.Red
        ElseIf speed3 = 0 Then
            lblSecondch.Text = "Down"
            lblSecondch.BackColor = Color.Black

        End If

    End If
    MessageBox.Show("Done .3")
End Sub

回答1:

The problem here is that you invoke the same methods once again, which are supposed to be run in threads. This causes the ping request to be sent yet another time, but this time the code runs on the UI thread (hence why it freezes). You should make sure to only invoke the code that updates the UI.

This is optional, but I recommend you to use an extension method to do the invocation check for you since it will improve readability but also decrease the amount of code you have to write:

Imports System.Runtime.CompilerServices

Public Module Extensions
    <Extension()> _
    Public Sub InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object())
        If Parameters Is Nothing OrElse _
            Parameters.Length = 0 Then Parameters = Nothing 'If Parameters is null or has a length of zero then no parameters should be passed.
        If Control.InvokeRequired = True Then
            Control.Invoke(Method, Parameters)
        Else
            Method.DynamicInvoke(Parameters)
        End If
    End Sub
End Module

Now, if you target .NET Framework 4.0 (or higher) you can use a lambda expression for a quick, inline delegate:

Me.InvokeIfRequired( _
    Sub()
        If speedmodem >= 1 AndAlso speedmodem <= 500 Then
            lblModCh.BackColor = Color.Green
        ElseIf speedmodem >= 501 And speedmodem <= 1500 Then
            lblModCh.BackColor = Color.Orange
        ElseIf speedmodem >= 1501 Then
            lblModCh.BackColor = Color.Red
        ElseIf speedmodem = 0 Then
            lblModCh.BackColor = Color.Black
        End If
    End Sub)

However if you target .NET Framework 3.5 or lower you have to create delegates the normal way:

Private Delegate Sub UpdatePingStatusDelegate(ByVal speedmodem As Integer)

Private Sub PingPublicTH()
    ...your code...

    Me.InvokeIfRequired(New UpdatePingStatusDelegate(AddressOf UpdateStatusPublicTH), speedmodem)
End Sub

Private Sub UpdateStatusPublicTH(ByVal speedmodem As Integer)
    If speedmodem >= 1 AndAlso speedmodem <= 500 Then
        lblModCh.BackColor = Color.Green
    ElseIf speedmodem >= 501 And speedmodem <= 1500 Then
        lblModCh.BackColor = Color.Orange
    ElseIf speedmodem >= 1501 Then
        lblModCh.BackColor = Color.Red
    ElseIf speedmodem = 0 Then
        lblModCh.BackColor = Color.Black
    End If
End Sub

Note:

  • When using the extension method InvokeIfRequired you don't need to check Control.InvokeRequired in the rest of your code. You only require the one call to the extension method and it will do the checking for you.

  • If you use my second method you only need the one UpdatePingStatusDelegate delegate if all you need is one integer to update the status from all the threads.

Please also see the difference between And and AndAlso.