阅读的SerialPort因为不拥有线程的原因错误
-
22-07-2019 - |
题
我有一个简单的WPF应用程序窗口试图读取与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线程有处理窗口消息(例如鼠标点击等)的消息泵。您的串口不欢送窗口消息。当数据进入的串行端口上从UI线程一个完全不同的线程被用于处理该消息。
因此,在应用程序中,该mSerialPort_DataReceived方法是在不同的线程比你的UI线程上执行。您可以使用线程调试窗口,以验证这一点。
当您尝试更新UI,您要修改与从不同的线程,它抛出你见过异常的UI线程的线程亲和力的控制。
TL; DR:您正在试图修改UI线程之外的UI元素。使用
txtSerialOutput.Dispatcher.Invoke
要运行在UI线程上的更新。 这里有一个关于如何做到thisin这个社区内容的例子页。
在调度程序将调用你在UI线程上的方法(它发送一个窗口消息到UI说:“海guize,运行此方法kthx”),然后你的方法可以安全地更新从UI线程的UI。
其他提示
我添加这样的:
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可以仅由主应用程序线程进行更新。串行端口事件的异步回调在后台处理上一个单独的线程。威尔提到的,可以使用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几个地方,因为你不必有线程检查整个洒您的代码。