如何从VB中的另一个线程在主线程上调用函数 [英] How do I call a function on the main thread from another thread in vb

查看:164
本文介绍了如何从VB中的另一个线程在主线程上调用函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个计时器函数,该函数在Windows窗体上调用,当单击UI上的按钮时,该函数可以完美工作.但是,在我的应用程序Im使用TCP套接字连接到服务器时,当服务器断开Im的连接时,它会捕获抛出的异常和Id,就像此时要启动计时器(ticker),然后运行重新连接. 当我从try catch中引用计时器时,它不会运行,所以我可以想象它是因为它在主线程上?

I have a timer function which is called on my windows form which works perfectly when a button on the UI is clicked. However, in my application Im connecting to a server using a TCP socket, when the server disconnects Im catching the exception thrown and Id like at this point for the timer to start (ticker) then run the reconnection. When i reference the timer from the try catch it wont run, so I imagine its because its on the main thread?

这是我的计时器代码: 公共委托子DroppedConnectionDelegate(ByVal ReConn为整数)

Here is my code for the timer: Public Delegate Sub DroppedConnectionDelegate(ByVal ReConn As Integer)

 Public Sub DroppedConnection(ByVal ReConn As Integer)

    Console.Write("Dropped Connection()")

    Thread.Sleep(1000)
    If ReConn = 1 Then

            MakeConnection(My.Settings.savedIP, False)
        Else
            isRunning = False
            Second = 0
            'client.Close()
            'client.Dispose()
            Timer1.Interval = 1000
            Timer1.Enabled = True

            Timer1.Start() 'Timer starts functioning


        End If
    '  End If

End Sub

Public Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    Second = Second + 1
    Console.WriteLine(Second)
    If Second >= 10 Then
        Timer1.Stop() 'Timer stops functioning
        Timer1.Enabled = False
        MakeConnection(ServerAddressString, False)
    End If

End Sub

这里是我想在catch异常时调用计时器的子项:

Here is the sub from where Id like to call the timer on catch exception:

 Public Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)

        Dim state As StateObject = CType(ar.AsyncState, StateObject)
            Dim client As Socket = state.workSocket
            Dim strReceiveBuffer As String = String.Empty

            ' Read data from the remote device.
            Dim bytesRead As Integer

        Console.WriteLine("ar to string = " & ar.ToString)
        'While client.Connected
        If (AsynchronousClient.connectDone.WaitOne(5000, True)) Then
            Try

                If client.Connected = True Then

                    bytesRead = client.EndReceive(ar)
                    Console.WriteLine("Socket Connected")
                    If bytesRead > 0 Then
                        state.sb.Clear()
                        ' There might be more data, so store the data received so far.
                        state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead))

                        strReceiveBuffer = state.sb.ToString()
                        MainForm.main_Response = state.sb.ToString()

                        If strReceiveBuffer.IndexOf("doses-remaining") > 0 Then
                            response = state.sb.ToString()
                            MainForm.GetLabelTextToString(response)
                            'receiveDone.Set()
                            'isReceived = True
                            'Return
                        End If

                        ' Get the rest of the data.
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)


                    Else
                        ' All the data has arrived; put it in response.
                        If state.sb.Length > 1 Then

                            response = state.sb.ToString()

                        End If
                        ' Signal that all bytes have been received.
                        receiveDone.Set()
                        isReceived = True
                    End If
                Else
                    MainForm.WriteLog("RecieveCallback() Error outside catch :" & Date.Today.ToString & " " & TimeOfDay)
                    MainForm.UpdateList("RecievecallBack error, attempting reconnect..." & client.RemoteEndPoint.ToString())

                    MainForm.isRunning = False
                    MainForm.DroppedConnection(0)
                End If


            Catch ex As Exception

                'MessageBox.Show("ReceiveCallback Error, Check Log.")

                MainForm.WriteLog("RecieveCallback() Error inside catch :" & Date.Today.ToString & " " & TimeOfDay & ex.Message)
                MainForm.UpdateList("RecievecallBack error, attempting reconnect..." & client.RemoteEndPoint.ToString())

                client.Shutdown(SocketShutdown.Both)
                client.Close()
                client.Dispose()
                MainForm.isRunning = False
                Dim d As DroppedConnectionDelegate = AddressOf MainForm.DroppedConnection
                'MainForm.DroppedConnection(0)
                d.BeginInvoke(0, Nothing, Nothing)
                Exit Try



            End Try

        End If
        ' End While
    End Sub 'ReceiveCallback

如果我运行该应用程序并且服务器意外断开连接,则当前它不会自动重新连接.但是,如果我单击连接"按钮(仍然会调用计时器),它将运行计时器

If I run the application and the server disconnects unexpectedly, currently it wont reconnect automatically. But it will run the timer if I click the connect button (which calls the timer anyway)

任何人都可以帮忙吗?

预先感谢

推荐答案

我不确定自己从未尝试过100%,但是我认为如果您使用Timers.Timer而不是Windows.Forms.Timer,那么您会现在正在工作.默认情况下,Timers.Timer将在辅助线程上引发其Elapsed事件,但是您可以为其SynchronizingObject属性分配表单或其他控件以使其在UI线程上引发事件.在这种情况下,从辅助线程调用Start应该可以.

I'm not 100% sure as I've never tried but I think that if you use a Timers.Timer instead of a Windows.Forms.Timer then what you're doing now will work. By default, a Timers.Timer will raise its Elapsed event on a secondary thread but you can assign a form or other control to its SynchronizingObject property to make it raise events on the UI thread. Calling Start from a secondary thread should be OK in that case.

如果这不起作用,那么您可以通过在启动Timer之前首先编组对UI线程的方法调用来进行所需的操作.可能看起来像这样:

If that doesn't work then you can do what you want by first marshalling a method call to the UI thread before starting the Timer. That might look like this:

Private Sub StartTimer()
    If Me.InvokeRequired Then
        Me.Invoke(New Action(AddressOf StartTimer))
    Else
        Me.Timer1.Start()
    End If
End Sub

您可以在任何线程上调用该方法,它将起作用.

You can call that method on any thread and it will just work.

请注意,设置Enabled属性并调用StartStop是多余的. Start已将Enabled设置为True,并且Stop将其设置为False.做一个或另一个,不要两个都做.当您知道要启动或停止Timer的事实时,最合适的方法是调用方法.如果您的Boolean值可能是TrueFalse,则应使用该属性.使用文字值设置属性本身并不是错误的,但这不是预期的.当您使用相同的方法从辅助线程启动和停止Timer时,使用该属性的位置的一个示例:

Note that setting the Enabled property and calling Start or Stop is redundant. Start already sets Enabled to True and Stop sets it to False. Do one or the other, not both. It is most appropriate to call a method when you know for a fact that you want to either start or stop the Timer. If you have a Boolean value that might be either True or False then you should use the property. Setting the property using a literal value is not wrong per se but it is not what was intended. An example of where it's appropriate to use the property is when you use the same method to both start and stop the Timer from a secondary thread:

Private Sub EnableTimer(enable As Boolean)
    If Me.InvokeRequired Then
        Me.Invoke(New Action(Of Boolean)(AddressOf EnableTimer), enable)
    Else
        Me.Timer1.Enabled = enable
    End If
End Sub

同样,在任何线程上调用该方法都将起作用.

Again, calling that method on any thread will just work.

这篇关于如何从VB中的另一个线程在主线程上调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆