[英]SerialPort reading cause error because of not owning thread
我有一個簡單的WPF Windows應用程序,嘗試使用System.IO.Ports.SerialPort
讀取串行端口。
當我嘗試讀取DataReceived
事件中的傳入數據時,出現異常,表明我無權訪問該線程。 我該如何解決?
我在WPF窗口類中有這個:
Public WithEvents mSerialPort As New SerialPort()
Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnConnect.Click
With mSerialPort
If .IsOpen Then
.Close()
End If
.BaudRate = 4800
.PortName = SerialPort.GetPortNames()(0)
.Parity = Parity.None
.DataBits = 8
.StopBits = StopBits.One
.NewLine = vbCrLf
.Open()
End With
End Sub
Private Sub mSerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles mSerialPort.DataReceived
If e.EventType = SerialData.Chars Then
txtSerialOutput.Text += mSerialPort.ReadExisting()
End If
End Sub
Protected Overrides Sub Finalize()
If mSerialPort.IsOpen Then
mSerialPort.Close()
End If
mSerialPort.Dispose()
mSerialPort = Nothing
MyBase.Finalize()
End Sub
當DataReceived
事件觸發時,我在mSerialPort.ReadExisting()
上收到以下異常:
System.InvalidOperationException was unhandled
Message="The calling thread cannot access this object because a different thread owns it."
Source="WindowsBase"
StackTrace:
at System.Windows.Threading.Dispatcher.VerifyAccess() at System.Windows.Threading.DispatcherObject.VerifyAccess() at System.Windows.DependencyObject.GetValue(DependencyProperty dp) at System.Windows.Controls.TextBox.get_Text() at Serial.Serial.mSerialPort_DataReceived(Object sender, SerialDataReceivedEventArgs e) in D:\SubVersion\VisionLite\Serial\Serial.xaml.vb:line 24 at System.IO.Ports.SerialPort.CatchReceivedEvents(Object src, SerialDataReceivedEventArgs e) at System.IO.Ports.SerialStream.EventLoopRunner.CallReceiveEvents(Object state) at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack) at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)
歡迎來到多重閱讀的神奇世界!!!
發生的事情是您的所有UI元素(類實例)只能由UI線程訪問/更新。 我不會詳細介紹該線程關聯性,但是它是一個重要的主題,您應該檢查一下。
串行端口上輸入數據的事件發生在與UI線程不同的線程上 。 UI線程具有處理Windows消息(如鼠標單擊等)的消息泵。 您的串行端口不會發送Windows消息。 當數據進入串行端口時,將使用與UI線程完全不同的線程來處理該消息。
因此,在您的應用程序中,mSerialPort_DataReceived方法在與UI線程不同的線程中執行。 您可以使用“線程”調試窗口進行驗證。
當您嘗試更新UI時,您試圖修改具有與來自其他線程的UI線程的線程相似性的控件,這將引發您所看到的異常。
TL; DR:您正在嘗試在UI線程之外修改UI元素。 采用
txtSerialOutput.Dispatcher.Invoke
在UI線程上運行更新。 在此頁面的社區內容中,有一個有關如何執行此操作的示例。
Dispatcher將在UI線程上調用您的方法(它向Windows發送一條Windows消息,提示“ Hai guize,運行此方法kthx”),然后您的方法可以從UI線程安全地更新UI。
根據Will的回答,我現在解決了我的問題。 我認為問題出在訪問mSerialPort.ReadExisting()
,而問題實際上出在從DataReceived
事件內部訪問GUI元素txtSerialOutput
,該事件在單獨的線程中運行。
我添加了這個:
Private mBuffer As String = ""
Delegate Sub DelegateSetUiText()
Private Sub UpdateUiFromBuffer()
txtSerialOutput.Text = mBuffer
End Sub
...並且我將DataReceived
事件更改為此:
Private Sub mSerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles mSerialPort.DataReceived
If e.EventType = SerialData.Chars Then
mBuffer += mSerialPort.ReadExisting()
txtSerialOutput.Dispatcher.Invoke(New DelegateSetUiText(AddressOf UpdateUiFromBuffer))
End If
End Sub
UI只能由主應用程序線程更新。 串行端口事件的異步回調在單獨的線程中在后台處理。 如Will所述,您可以使用Dispatcher.Invoke在UI線程上將UI組件屬性更改排隊。
但是,由於您使用的是WPF,因此有一個使用綁定的更優雅,更慣用的解決方案。 假設您在串行端口上接收的數據對業務對象具有重要價值,則可以讓DataReceived事件更新對象中的屬性,然后將UI綁定到該屬性。
用粗糙的代碼:
Public Class MySerialData
Implements System.ComponentModel.INotifyPropertyChanged
Public Event PropertyChanged(sender as Object, e as System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotfifyPropertyChanged.PropertyChanged
private _serialData as String
Public Property SerialData() As String
Get
Return _serialData
End Get
Set(value as String)
If value <> _serialData Then
_serialData = value
RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("SerialData"))
End If
End Property
然后,在XAML文件中,可以將文本框綁定到該對象屬性:
<TextBox Text="{Binding Path=SerialData}"/>
假定DataContext設置為MySerialData類的實例。 做這些額外的工作的最大好處是WPF現在將自動為您處理所有跨線程封送處理,因此您不必擔心哪個線程正在調用UI更改,WPF中的綁定引擎使它變得更容易工作。 顯然,如果這只是一個廢棄項目,那么可能不需要額外的前期代碼。 但是,如果您要進行大量異步通信並更新UI,則WPF的此功能將為您節省大量時間,並且消除了多線程應用程序常見的大量錯誤。 我們在執行大量TCP通信的高線程應用程序中使用WPF,並且WPF中的綁定非常出色,尤其是當通過網絡傳輸的數據旨在更新UI中的多個位置時,因為您不必在整個過程中進行線程檢查您的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.