【问题标题】:Update form from an async event (websocket) from other thread (VBNET)从其他线程 (VBNET) 的异步事件 (websocket) 更新表单
【发布时间】:2020-12-09 12:15:05
【问题描述】:

在收到来自另一个类的异步触发事件的消息后,我正在尝试更新表单上的标签,但到目前为止没有任何效果。

嗯,一个有效的方法是在主线程上添加一个计时器,它每 200 毫秒使用另一个类的公共变量更新标签。但一定有别的办法。

我尝试使用调用方法,但也没有用。

我错过了什么/做错了什么?

编辑:下面的函数被调用:

Await SubscribeToWebsocketEvents(creds)

功能:

Public Shared Async Function SubscribeToWebsocketEvents(ByVal creds As Credentials) As Task
    Dim socket = New CoinbaseProWebSocket(New WebSocketConfig With {
    .ApiKey = creds.ApiKey,
    .Secret = creds.ApiSecret,
    .Passphrase = creds.ApiPassphrase,
    .SocketUri = "wss://ws-feed-public.sandbox.pro.coinbase.com"
})

    WriteLine(">> Connecting websocket...")

    Dim result = Await socket.ConnectAsync()

    If Not result.Success Then Throw New Exception("Connect error.")
    WriteLine(">> Connected.")

    AddHandler socket.RawSocket.Closed, (AddressOf Websocket_Closed)
    AddHandler socket.RawSocket.Error, (AddressOf Websocket_Error)
    AddHandler socket.RawSocket.MessageReceived, (AddressOf Websocket_MessageReceived)

    Dim Subsc = New Subscription

    Subsc.ProductIds.AddRange({"BTC-EUR", "BTC-USD"})
    Subsc.Channels.Add("ticker")
    Subsc.Channels.Add("matches")

    WriteLine(">> Subscribing to events...")
    Await socket.SubscribeAsync(Subsc)
    WriteLine(">> Subscribed.")
End Function

事件:

Private Shared Sub Websocket_MessageReceived(ByVal sender As Object, ByVal e As WebSocket4Net.MessageReceivedEventArgs)
    WriteLine("Message received.")

    Dim msg = Nothing, hb As HeartbeatEvent = Nothing, tk As TickerEvent = Nothing

    Form1.BitcoinPriceLabel.Text = "Test to see if I can edit the label"

    If WebSocketHelper.TryParse(e.Message, msg) Then
        If CSharpImpl.__Assign(hb, TryCast(msg, HeartbeatEvent)) IsNot Nothing Then
            '     WriteLine($"Sequence: {hb.Sequence}, Last Trade Id: {hb.LastTradeId}")
        End If
        If CSharpImpl.__Assign(tk, TryCast(msg, TickerEvent)) IsNot Nothing Then
            If tk.ProductId = "BTC-EUR" Then
                WriteLine($"Coin: {tk.ProductId}, Last value: {tk.Price}, BestAsk: {tk.BestAsk}")
            End If
        End If
    End If
End Sub

【问题讨论】:

    标签: vb.net multithreading variables websocket


    【解决方案1】:

    问题是您在辅助线程上使用Form1 的默认实例。默认实例是特定于线程的,因此您更新的实例不是您显示的实例。阅读this了解更多信息。

    解决方案是在 UI 线程上调用一个方法,然后您使用的默认实例将与您显示的相同。如果您所在的类不是表单或其他控件,则可以使用 SynchronizationContext 类,例如

    Private context As SynchronizationContext = SynchronizationContext.Current
    
    'This method is executed on a secondary thread.
    Private Sub BackgroundMethod()
        context.Post(AddressOf ForegroundMethod, Nothing)
    End Sub
    
    'This method is executed on the UI thread.
    Private Sub ForegroundMethod(state As Object)
        'Update the UI here.
    End Sub
    

    您需要在 UI 线程上创建该类型的实例,然后它获取的当前上下文将是 UI 线程的上下文。该上下文用于稍后将委托发布到该线程。您为其创建委托的方法必须有一个 Object 类型的参数。当您调用Post 时,将您需要的任何数据传递给该参数,然后在发布的方法中转换为适当的类型。在您的情况下,该方法可能如下所示:

    Private Sub SetForm1BitcoinPriceLabelText(text As Object)
        Form1.BitcoinPriceLabel.Text = text
    End Sub
    

    你可以这样称呼它:

    context.Post(AddressOf SetForm1BitcoinPriceLabelText, "Test to see if I can edit the label")
    

    【讨论】:

    • 就是这样!非常感谢,我已经“浪费”了 10 个小时,所以我很高兴终于让它工作了。仍在尝试找出它是如何工作的:D
    • @BWS,阅读this 了解一些背景信息。它展示了如何确保代码在表单中的 UI 线程上执行。还有一篇关于在 WPF 窗口中执行相同操作的文章。 SynchroniizatiionContext 类实际上在内部创建了一个 WinForms 或 WPF 控件,并在一个不能直接访问控件本身的类中使用它来执行相同的操作。 SendPost 方法实际上在该内部控制上调用 InvokeBeginInvoke
    • @BWS,如果答案已经解决了您的问题,请单击旁边的复选标记接受它。这告诉所有人,您无需打开问题并阅读您的评论即可找到更多帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-18
    • 1970-01-01
    • 2017-12-14
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多