How to work around Cross-thread operation
I am creating a CHAT interface for my game (when networking, server/client style) and I ran into a little problem...
The way it works... I have forms for the SERVER (frmServer) and CLIENTS (frmClient) each of them have a LISTBOX (lbChat) in which I input (lbChat.Items.Add(string)) text as the users type it - in essence a chatbox...
So, let's use the CLIENT as an example (as it is simpler and the concept the same in both client and server cases)... The Client form (frmClient) creates a thread that is used to listen for chat messages (via TCP) - so what I did was pass the listbox itself (lbChat) to the thread thus (I thought) solving my problem of writing into the listbox when chat messages arrive (and are caught by the thread and not frmClient of course)... Now while debugging the code I get the following exception when my thread attempts to write in the listbox (lbChat) of frmClient General Exception: System.InvalidOperationException: Cross-thread operation not valid: Control 'lbChat' accessed from a thread other than the thread it was created on.
So now the question is how do I work around this cross-thread exception? How do I pass information (chat text) from my listener threads (and there will be multiple of them in the field, one per client) to the centralized listbox (lbChat) on the form? I need some kind of method to transfer information while also reducing the chances of running into contention issues (do I need to use ReaderWriterLock?), thing is I have no clue how to accomplish this task (I thought simply passing in the listbox would allow me to write to it in each of the threads)...
Any ideas, hints, and help would be greatly appreciated, thanks this is code for client
Imports System.Net.Sockets
Imports System.Windows.Forms
Imports System.IO
Imports System.Threading
Public Class recieve
Private reader As BinaryReader
Private message As String = ""
Private frmObject As frmMain
Private readthred As Thread
Private Delegate Sub af(ByVal item As String)
Public Sub fun(ByVal item As String)
If (frmMain.ListBox1.InvokeRequired()) Then
frmMain.ListBox1.Invoke(New af(AddressOf fun))
frmMain.ListBox1.Items.Add(item)
Else
frmMain.ListBox1.Items.Add(item)
End If
End Sub
Public Sub New(ByVal frmObject As frmMain)
MyBase.New()
Me.frmObject = frmObject
readthred = New Thread(AddressOf runClinet)
readthred.SetApartmentState(ApartmentState.STA)
readthred.Start()
End Sub
Public Sub runClinet()
Dim client As TcpClient
Try
client = New TcpClient()
client.Connect("127.0.0.1", 5234)
reader = New BinaryReader(client.GetStream())
Try
Dim Path As String
Path = reader.ReadString
frmMain.ListBox1.SelectedItem = Path
frmObject.Playlist.SelectedItem = frmObject.ListBox1.SelectedItem
frmMain.AxWindowsMediaPlayer1.URL = Path
frmObject.ListBox1.Items.Add(Path)
frmObject.Playlist.Items.Add(Path)
Try
While True
Path = reader.ReadString
Select Case Path
Case "1"
frmObject.AxWindowsMediaPlayer1.Ctlcontrols.pause()
Case "2"
frmObject.AxWindowsMediaPlayer1.Ctlcontrols.play()
Case "3"
frmObject.AxWindowsMediaPlayer1.Ctlcontrols.stop()
End Select
End While
Catch ex As Exception
End Try
Catch inputoutputException As IOException
Finally
End Try
开发者_StackOverflow社区 Try
frmObject.AxWindowsMediaPlayer1.Ctlcontrols.stop()
reader.Close()
client.Close()
Catch exx As Exception
End Try
Catch inputoutputException As IOException
End Try
End Sub
End Class
Call Me.BeginInvoke( ... )
passing a delegate that points to the function that should be called on the UI thread.
E.g.:
Me.BeginInvoke(New MyDelegate(AddressOf DelegateMethod), parameterArray)
BeginInvoke and Invoke pass a delegate to be invoked to the UI thread.
EDIT
In the runClient method you access the listbox, playlist and the form itself. This method is running on an other thread than the form was created on so all these calls must be switched to the UI thread using BeginInvoke or Invoke
精彩评论