我有一个Windows窗体,通过串口从秤中获取数据。由于我需要操作员能够取消该过程,所以我在第二个线程上执行get数据处理。
数据收集过程一次从秤中获取多个读数。表单有一个标签,需要使用特定于每次阅读的信息进行更新。
我使用此代码调用方法从秤中获取数据。
Dim ad As New readALine(AddressOf thisScale.readALine)
Dim ac As New AsyncCallback(AddressOf Me.GetDataCallback)
Dim iar As IAsyncResult = ad.BeginInvoke("", ac, ad)
readALine方法的委托在UI代码中定义。
Delegate Function readALine(ByVal toSend As String) As String
GetDataCallback方法:
Private Sub GetDataCallback(ByVal ia As IAsyncResult)
.
.
.
lblInstructions.Text = _thisMeasure.dt.Rows(_currRow - 1).Item("cnt") & ":"
lblInstructions.Refresh()
.
.
.
End Sub
我在“lblInstructions.Text =”语句中得到了异常。
我认为GetDataCallback方法是UI线程的一部分,所以不明白我为什么会得到异常。我知道这可能是使用BackgroundWorker重写的,这是适当的事件,但现在想了解为什么这不能按预期工作。
该应用程序最初是在VS2003中编写的,最近刚升级到VS2008。
任何见解都将受到赞赏。
谢谢, 戴夫
答案 0 :(得分:3)
问题是对BeginInvoke
的混淆。调用Control.BeginInvoke封送对UI线程的委托调用。在委托上调用BeginInvoke
会导致它在线程池线程上执行 - 并且您提供的任何回调都在同一个(线程池)线程上执行。后者就是你正在做的,这就是为什么GetDataCallback
正在线程池线程上执行,而不是UI线程..
因此,在GetDataCallback
中,您需要调用Control.Invoke
或Control.BeginInvoke
来封送回UI线程。
有一点需要注意:这些天我很少使用Control.InvokeRequired
- 无条件调用Invoke
/ BeginInvoke
更为简单;根据我的经验,性能差异通常不足以弥补可读性的好处。
答案 1 :(得分:1)
用户界面控件只能由创建它们的线程更新
试
yourForm.BeginInvoke
代替;应该将调用编组到正确的线程
答案 2 :(得分:0)
在.NET 1.1中,可以执行这些跨线程操作,即使它们不安全。
在.NET 2.0中,当您尝试执行此类的跨线程操作时,抛出了您提到的异常,这意味着您正在非UI线程上执行UI操作,即使您认为自己并非如此吨。
尝试使用Me.InvokeRequired
方法确定您当前是否在UI线程上。例如。您可以定义一个方法来执行必要的任务:
Protected Sub PerformUIOperations() If Me.InvokeRequired Then 'We are currently on a non-UI thread. Invoke this method on the UI thread: Me.Invoke(New MethodInvoker(AddressOf Me.PerformUIOperations)) Return End If 'Perform UI operations when we know it is safe: lblInstructions.Text = "blabla" End Sub
然后可以从任何非UI线程调用PerformUIOperations
方法,因为它负责在正确的线程本身上执行任务。
当然,如果您需要将参数传递给PerformUIOperations方法(例如有关正在进行的操作的信息),您必须定义一个委托以匹配PerformUIOperations方法签名并使用它而不是MethodInvoker。
答案 3 :(得分:0)
乔,
我在代码的另一部分中有以下内容:
Delegate Sub setValueCallback(ByVal row As Integer, ByVal value As Decimal)
Public Sub setValue(ByVal row As Integer, ByVal value As Decimal)
If Me.Controls.Item(_tbIndex(row - 1)).InvokeRequired Then
Dim d As New setValueCallback(AddressOf setValue)
Me.Invoke(d, New Object() {row, value})
Else
Dim tb As TextBox = Me.Controls.Item(_tbIndex(row - 1))
tb.Text = value
_dt.Rows(tb.Tag).Item(1) = value
End If
End Sub
如何将其重写为不使用.InvokeRequired?
答案 4 :(得分:0)
戴夫,也许这就是你要找的解决方案:
Dim ad As New readALine(AddressOf thisScale.readALine)
Dim ac As New AsyncCallback(AddressOf Me.GetDataCallback)
Dim iar As IAsyncResult = ad.BeginInvoke("", ac, ad)
Delegate Function readALine(ByVal toSend As String) As String
Private Sub GetDataCallback(ByVal ia As IAsyncResult)
If lblInstructions.InvokeRequired Then
lblInstructions.Invoke(New AsyncCallback(AddressOf GetDataCallback), New Object() {ia})
Else
.
.
lblInstructions.Text = _thisMeasure.dt.Rows(_currRow - 1).Item("cnt") & ":"
lblInstructions.Refresh()
.
.
End If
End Sub