【问题标题】:Unable to append RichTextBox text in Cross-Thread operation VB.NET无法在跨线程操作 VB.NET 中附加 RichTextBox 文本
【发布时间】:2014-05-27 20:02:15
【问题描述】:

我正在编写一个多线程 TCP 回显服务器,但问题是当我从 ClientHandler 类调用 UpdateMessage 时,RichTextBox 的文本没有附加。

这是包含 RichTextBox1 的 Form1 类

公开课表1

Const PORT As Integer = 1234 'The port number on which the server will listen for connection.
Dim ServerSocket As New TcpListener(PORT) 'The Server Socket that will listen for connections on specified port number.

Dim Link As TcpClient 'The Socket that will handle the client.

Dim NumberOfClients As Integer = 0 'The total number of clients connected to the server.


Dim myThread As Thread 'The thread on which the server will handle the client


Dim sc As SynchronizationContext = SynchronizationContext.Current

Public Clienthandler As ClientHandler

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles btnStart.Click
    ServerSocket.Start()
    ' UpdateMessage("Server started..")


    myThread = New Thread(AddressOf AcceptClients) 'Handle the client in a different thread
    myThread.Start()


    btnStart.Text = "Started!"
    btnStart.Enabled = False

End Sub




Private Sub AcceptClients()


    'Keep accepting and handling the clients.
    While (True)
        Link = ServerSocket.AcceptTcpClient() 'Accept the client and let the Socket deal with it.
        NumberOfClients = NumberOfClients + 1 'update the total number of clients

        Dim ClientAddress As String = Link.Client.RemoteEndPoint.ToString

        '  UpdateMessage("Client number " & NumberOfClients & " connected from " & ClientAddress)


        Clienthandler = New ClientHandler
        Clienthandler.StartClientThread(Link, NumberOfClients)



        Me.sc.Post(AddressOf Clienthandler.UpdateMessage, "Client number " & NumberOfClients & " connected from " & ClientAddress)

    End While

    'close the sockets

    Link.Close() 'close the Socket.
    ServerSocket.Stop() 'Stop the Server Socket.

End Sub

结束类

这是 ClientHandler 类:

公共类ClientHandler

Private Link As TcpClient
Private ClientNumber As Integer
Dim NumberOfMessages As Integer = 0 'The number of messages that client has sent.
Dim Stream As NetworkStream 'The stream being used for sending/receiving data over Socket.


Dim scHandler As SynchronizationContext = SynchronizationContext.Current

Public Sub StartClientThread(Link As TcpClient, ClientNumber As Integer)

    'Initialize the class variables with the arguments passed in StartClient 

    Me.Link = Link
    Me.ClientNumber = ClientNumber



    'Start the thread

    Dim ClientThread As New Thread(AddressOf Chat)
    ClientThread.Start()

    UpdateMessage("Thread for client " & ClientNumber & " started!")



End Sub

Public Sub UpdateMessage2(消息作为字符串)

    Me.scHandler.Post(AddressOf UpdateMessage2, Message)
End Sub
Public Sub UpdateMessage(Message As String)

    Message = vbNewLine & Trim(Message)

    If Message.Length < 1 Then
        Message = "empty"
    End If

    MessageBox.Show("Updating message!" & vbNewLine & Message)


    Form1.RichTextBox1.AppendText(Message)





End Sub

同样的问题仍然存在,RichTextBox 仅从 UI 线程更新。

【问题讨论】:

  • 在需要对象引用的地方使用类型名称 Form1是一个VB.NET陷阱。当您在线程中执行此操作时,它会停止工作,它会创建一个 Form1 类型的 new 对象。一个你看不到的,与你正在看的那个不匹配。您必须使用正确的对象引用。
  • 另外,我只是想指出,文本框代码应该位于您正在编码的任何内容的基类中。即:myClass 将包含一个成员TextBox,以及线程类和方法。然后将您班级中的TextBox 控件添加到Form1。这样你就有了线程类中文本框的引用。另外,您的对象/控件的所有代码都在一个类/命名空间中。

标签: vb.net multithreading


【解决方案1】:

比 Hans Passant 更具体一点,在预期对象的地方使用表单名称会使用表单的默认实例,这是系统为您创建和管理的实例。默认实例是特定于线程的,这意味着每个线程有一个默认实例。如果您在辅助线程中使用默认实例,那么您使用的对象与您在 UI 线程上显示的对象不同。您可以在这里阅读更多关于默认实例的信息:

http://jmcilhinney.blogspot.com.au/2009/07/vbnet-default-form-instances.html

您需要以某种方式将方法调用编组回 UI 线程,然后更新该线程上的现有表单实例。有几种方法可以做到:

  1. 可以访问现有的表单对象并调用其 Invoke 方法。
  2. 使用SynchronizationContext 类将方法调用编组到 UI 线程,然后使用默认实例。

【讨论】:

  • 简单技巧:维护SharedForm1 的引用。
  • @porkchop,这是一种快速而肮脏的解决方案,虽然可行,但并不值得骄傲。如果有机会,最好学习正确的做事方式,而不是满足于简单的 hack。
  • 这就是为什么我称它为黑客,你真的必须小心这种事情;p
  • @jmcilhinney 谢谢,我尝试使用 SynchrnizationContext,但我对线程概念不太熟悉,我仍然无法在我的代码中实现它。
  • 如果我不知道你做了什么,那么我不知道你做错了什么,但这里有一个使用 SynchronizationContext 的快速示例:vbforums.com/…
【解决方案2】:
Class myClass
    Friend TB As TextBox
    Class myThread
        ' ...
    End Class
End Class

Class Form1
    Overrides Sub OnLoad
        _tcpClient = New myClass
        Controls.Add(_tcpClient.TB)
        _tcpClient.doSomething()
    End Sub
End Class

就像我的 cmets 所说的那样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-02
    • 2011-07-11
    相关资源
    最近更新 更多