我有一個數據網格綁定到數據綁定列表的表單,該數據綁定列表由來自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