我有一个数据网格绑定到数据绑定列表的表单,该数据绑定列表由来自tcpip连接的恒定数据流填充。 TCPIP连接位于一个应该持续循环的线程上,并且当它找到足够的数据时创建类Data的一个实例并将其添加到(Data的)BindingList。我得到一个错误,说“跨线程操作无效:控制”从一个线程访问,而不是它创建的线程。“堆栈跟踪点,增加了新的数据(数据)的的BindingList行线程安全问题试图跨线程数据绑定
DataList.Insert(0, dataItem)
什么是奇怪,我是那么它不断去并正确填充我的数据网格。我对多线程编程不是很有经验,大部分代码都是异步的。我有一个datalock和一些互斥体(我刚学过的东西,但不知道我是否正确使用)。我在某处阅读使用事件会有所帮助,但如果NewData事件没有必要,我可以摆脱它。任何帮助使我的代码线程安全将不胜感激。
表格上的结合是这样的:
myDataGrid.datasource = myDataReader.DataList
这里是类启动胎面和读取数据(抱歉,我知道这是一个很大的代码,但我不想留下点什么出来这可能是重要的):
Public Class DataReader
Implements INotifyPropertyChanged
#Region "Properties"
Dim dataMutex As Mutex
Dim UnparsedMutex As Mutex
Dim mIP_Address As String
Dim convertingData As Boolean = False
Public Property IP_Address() As String
Get
Return mIP_Address
End Get
Set(ByVal value As String)
mIP_Address = value
End Set
End Property
Dim mPort As Integer
Public Property Port As Integer
Get
Return mPort
End Get
Set(ByVal value As Integer)
mPort = value
End Set
End Property
Private WithEvents mDataList As BindingList(Of Data)
Public ReadOnly Property DataList As BindingList(Of Data)
Get
Return mDataList
End Get
End Property
Public Event valueChanged As Eventhandler
Private Event NewDataAvaiable(ByVal newData As BindingList(Of Data))
Private mUnparsedData As String = ""
Private Property UnParsedData As String
Get
Return mUnparsedData
End Get
Set(ByVal value As String)
mUnparsedData = value
End Set
End Property
#End Region
#Region "Private Variables"
Dim mTCPIPClient As TcpClient
Dim mConnected As Boolean
Dim mTCPIPStream As NetworkStream
Dim mLastError As String
Dim mDataThread As System.Threading.Thread
Private dataLock As New Object
#End Region
#Region "Constructors"
Public Sub New(ByVal IPAddress As String, ByVal Port As Integer)
dataMutex = New Mutex(False, "MUTEXDATA")
UnparsedMutex = New Mutex(False, "MUTEXUNPARSEDDATA")
mIP_Address = IPAddress
mPort = Port
mConnected = False
mDataList = New BindingList(Of Data)
DataList.RaiseListChangedEvents = True
End Sub
#End Region
#Region "Events"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
#End Region
#Region "Methods"
Public Sub ConnectTCPIP()
Try
If Not mTCPIPClient Is Nothing AndAlso mTCPIPClient.Connected Then
mLastError = "Connection Error:Already connected"
Exit Sub
ElseIf String.IsNullOrEmpty(mIP_Address) Then
mLastError = "Connection Error:No IP Address"
Exit Sub
End If
DataList.Clear()
UnParsedData = String.Empty
mTCPIPClient = New TcpClient()
mTCPIPClient.Connect(mIP_Address, mPort)
If mTCPIPClient.Connected Then
mTCPIPClient.ReceiveTimeout = 500
mTCPIPClient.SendTimeout = 500
mTCPIPClient.LingerState = New System.Net.Sockets.LingerOption(False, 0)
mTCPIPClient.ReceiveBufferSize = 100000
mTCPIPClient.SendBufferSize = 100000
mTCPIPStream = mTCPIPClient.GetStream
LaunchDataThread()
If mDataThread.IsAlive Then mConnected = True
Else
mLastError = "Connection Error:Unknown Reason"
Exit Sub
End If
Catch ex As Exception
MessageBox.Show(ex.Message & ex.StackTrace)
End Try
End Sub
Public Sub Disconnect()
DisconnectTCPIP()
KillDataThread()
End Sub
Private Sub DisconnectTCPIP()
If Not mTCPIPClient Is Nothing AndAlso mTCPIPClient.Connected Then
mTCPIPClient.Close()
mTCPIPClient = Nothing
End If
End Sub
Public Function IsConnected() As Boolean
If mTCPIPClient Is Nothing OrElse mDataThread Is Nothing Then Return False
Return mTCPIPClient.Connected AndAlso mDataThread.IsAlive
End Function
Public Sub LaunchDataThread()
mDataThread = New System.Threading.Thread(AddressOf ReadData)
mDataThread.Start()
End Sub
Public Sub KillDataThread()
If Not mDataThread Is Nothing AndAlso mDataThread.IsAlive() Then
mDataThread.Abort()
End If
mDataThread = Nothing
End Sub
Public Sub ReadData()
Try
While True
Dim count As Short = 0
While Not mTCPIPClient.Connected AndAlso count <= 5
System.Threading.Thread.Sleep(5000)
ConnectTCPIP()
count += 1
End While
If mTCPIPClient.Connected = False Then Exit Sub
Dim dataBuffer(500) As Byte
Dim readBytes As Integer = 0
UnparsedMutex.WaitOne()
Do While mTCPIPStream.DataAvailable
readBytes = mTCPIPStream.Read(dataBuffer, 0, 500)
UnParsedData += System.Text.Encoding.ASCII.GetString(dataBuffer.Take(readBytes).ToArray())
Loop
UnparsedMutex.ReleaseMutex()
If UnParsedData.Length > 0 Then ProcessNewData()
System.Threading.Thread.Sleep(500)
End While
Catch ex As Exception
Windows.Forms.MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace)
End Try
End Sub
Public Sub ProcessNewData()
Dim dataToProcess As String = ""
UnparsedMutex.WaitOne()
If String.IsNullOrEmpty(UnParsedData) Then
UnparsedMutex.ReleaseMutex()
Exit Sub
End If
dataToProcess = UnParsedData.Substring(0, UnParsedData.LastIndexOf("PLC") + 3)
UnParsedData = UnParsedData.Remove(0, dataToProcess.Length)
UnparsedMutex.ReleaseMutex()
If Not String.IsNullOrEmpty(dataToProcess) Then
Dim matches = dataToProcess.Split(";"c)
If matches.Count > 0 Then
Dim newData As New BindingList(Of Data)
Try
For i = 0 To matches.Count - 1
newData.Insert(0, New Data(matches(i).Value))
Next
RaiseEvent NewDataAvaiable(newData)
Catch ex As Exception
Windows.Forms.MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace)
End Try
End If
End If
End Sub
Private Sub OnNewData(ByVal newData As BindingList(Of Data)) Handles Me.NewDataAvaiable
SyncLock dataLock
dataMutex.WaitOne()
For Each dataItem As Data In newData
DataList.Insert(0, dataItem)
If DataList.Count > 5000 Then DataList.RemoveAt(5000)
Next
dataMutex.ReleaseMutex()
End SyncLock
End Sub
#End Region
End Class
UPDATE:对的BindingList(的数据)将永远不需要从UI更新,从而改变数据的唯一线程应该是mDatathread中的DataReader类。
任何建议,甚至猜测,将不胜感激我需要得到这个去! – BinaryDuck