VB.NET 通用跨线程操作的两种不同方法;哪个更好?
VB.NET 2010、.NET 4
您好,
我最近读到有关使用 SynchronizationContext
对象来控制某些代码的执行线程的内容。我一直在使用通用子例程来处理(可能)跨线程调用,例如更新利用 Invoke
的 UI 控件。我是一名业余爱好者,很难理解任何特定方法的利弊。我正在寻找一些关于哪种方法更可取以及原因的见解。
更新:这个问题的部分原因是来自 Control.InvokeRequired 上的 MSDN 页面
。
更好的解决方案是使用
SynchronizationContext
返回SynchronizationContext
而不是 跨线程封送控制。
而且,当我环顾四周时,关于为什么大多数关于 SO 上此类问题的问题的答案都建议使用 Invoke
方法而不提及此方法,人们普遍感到困惑。
方法 1:
Public Sub InvokeControl(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of T))
If Control.InvokeRequired Then
Control.Invoke(New Action(Of T, Action(Of T))(AddressOf InvokeControl), New Object() {Control, Action})
Else
Action(Control)
End If
End Sub
方法 2:
Public Sub UIAction(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of Control))
SyncContext.Send(New Threading.SendOrPostCallback(Sub() Action(Control)), Nothing)
End Sub
其中 SyncContext
是在我的 UI 表单的构造函数中定义的 Threading.SynchronizationContext
对象(我将其存储在模块中...不确定这是否是最好的选择):
Public Sub New()
InitializeComponent()
SyncContext = WindowsFormsSynchronizationContext.Current
End Sub
然后,如果我想更新 UI 表单上的控件(例如,Label1
),我会这样做:
InvokeControl(Label1, Sub(x) x.Text = "hello")
或者
UIAction(Label1, Sub(x) x.Text = "hello")
所以,你们觉得怎么样?是首选一种方式还是取决于上下文?如果您有时间,冗长将不胜感激!
预先感谢,
布莱恩
VB.NET 2010, .NET 4
Hello,
I recently read about using SynchronizationContext
objects to control the execution thread for some code. I have been using a generic subroutine to handle (possibly) cross-thread calls for things like updating UI controls that utilize Invoke
. I'm an amateur and have a hard time understanding the pros and cons of any particular approach. I am looking for some insight on which approach might be preferable and why.
Update: This question is motivated, in part, by statements such as the following from the MSDN page on Control.InvokeRequired
.
An even better solution is to use the
SynchronizationContext
returned bySynchronizationContext
rather than a
control for cross-thread marshaling.
And also, general confusion as to why, as I look around, a majority of answers to questions regarding this type of problem on SO suggest the Invoke
approach without mentioning this method.
Method 1:
Public Sub InvokeControl(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of T))
If Control.InvokeRequired Then
Control.Invoke(New Action(Of T, Action(Of T))(AddressOf InvokeControl), New Object() {Control, Action})
Else
Action(Control)
End If
End Sub
Method 2:
Public Sub UIAction(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of Control))
SyncContext.Send(New Threading.SendOrPostCallback(Sub() Action(Control)), Nothing)
End Sub
Where SyncContext
is a Threading.SynchronizationContext
object defined in the constructor of my UI form (I store it in a module... Not sure if that's the best choice):
Public Sub New()
InitializeComponent()
SyncContext = WindowsFormsSynchronizationContext.Current
End Sub
Then, if I wanted to update a control (e.g., Label1
) on the UI form, I would do:
InvokeControl(Label1, Sub(x) x.Text = "hello")
or
UIAction(Label1, Sub(x) x.Text = "hello")
So, what do y'all think? Is one way preferred or does it depend on the context? If you have the time, verbosity would be appreciated!
Thanks in advance,
Brian
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我一直在做一些阅读,因为我没有得到任何答复,我想我应该开始对我自己的问题进行部分回答,其中包含我迄今为止发现的内容:
我发现了一个 有趣的 codeproject 文章 讨论
SynchronizationContext< 的使用/code> 用于在线程之间编组代码(特别是从工作线程到 UI 线程)。我发现一些有趣的观察结果:
SynchronizationContext
不是SynchronizationContext
类的实例,而是派生的System.Windows.Forms.WindowsFormsSynchronizationContext
类的实例来自SynchronizationContext
。此类定义了Post
/Send
的行为,允许将代码从一个线程编组到另一个线程。SynchronizationContext
而不是使用Invoke
的一个好处是,您不必在逻辑中保留对 UI 表单的引用来调用它。Post
方法对于完成指标更新之类的事情似乎很有吸引力,因为它是非阻塞的,但是,正如文章指出的,发布代码中引发的异常会在 UI 线程中引发。即,发布到 UI 的代码中的错误可能会使 UI 崩溃。Send
没有这个问题。发送时抛出的异常在工作线程中抛出。更新:这是 另一篇富有洞察力的文章。在本文中,Kael Rowan 讨论了一种上下文,在该上下文中使用
SynchronizationContext
可能比控件实例的Invoke
/BeginInvoke
方法更好。他认为,在编写可重用库时,仅仅出于调用目的而必须维护对库外部控件的引用是不可取的。他提供了委托代码,确保创建的任何新线程都将共享 UI 线程的SynchronizationContext
。好吧,看来我不会再在这里收到任何评论了。我在这里写的内容是我的无知使我能够得到的最接近的答案。如果有人还有什么要补充的,我肯定会很感激,但现在我要继续了。 :/
Well, I've been doing some reading and, since I'm not getting any responses, I figured I'd start a partial answer to my own question containing what I've found so far:
I found an interesting codeproject article discussing the use of
SynchronizationContext
for marshaling code between threads (and specifically from worker threads to the UI thread). Some observations I found interesting:SynchronizationContext
object is created upon creation of the first control in that thread. Before that, it is not defined.SynchronizationContext
for the UI thread is not an instance of theSynchronizationContext
class, but of theSystem.Windows.Forms.WindowsFormsSynchronizationContext
class which is derived fromSynchronizationContext
. It is this class that defines the behavior ofPost
/Send
allowing marshaling of code from one thread to another.SynchronizationContext
around rather than usingInvoke
is that you don't have to keep a reference to your UI form in logic in order to invoke it.Post
method seems appealing for accomplishing things like indicator updates since it's non-blocking, but, as the article points out, exceptions thrown in posted code are thrown in the UI thread. i.e., a bug in code posted to the UI can crash the UI.Send
doesn't have this problem. Exceptions thrown when sending are thrown in the work thread.Update: Here is another insightful article. In this article, Kael Rowan discusses a context in which using
SynchronizationContext
might be preferable to a control instance'sInvoke
/BeginInvoke
methods. He argues that, when writing a reusable library, it is not desirable to have to maintain a reference to a control outside of the library simply for invokation purposes. He provides code for a delegate that ensures any new thread created will share the UI thread'sSynchronizationContext
.Alright, well, it looks like I'm not going to get any more comments here. What I've written here is about as close as my ignorance allows me to get to an answer. If anyone has anything else to add, I'd surely appreciate it, but I'm moving on for now. :/