调用跨线程事件的最干净方法
时间:2020-03-05 18:42:01 来源:igfitidea点击:
我发现.NET事件模型是如此,我经常会在一个线程上引发一个事件,然后在另一个线程上监听它。我想知道将事件从后台线程封送到我的UI线程的最干净方法是什么。
根据社区的建议,我使用了以下方法:
// earlier in the code mCoolObject.CoolEvent+= new CoolObjectEventHandler(mCoolObject_CoolEvent); // then private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args) { if (InvokeRequired) { CoolObjectEventHandler cb = new CoolObjectEventHandler( mCoolObject_CoolEvent); Invoke(cb, new object[] { sender, args }); return; } // do the dirty work of my method here }
解决方案
回答
我回避了多余的委托人声明。
private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args) { if (InvokeRequired) { Invoke(new Action<object, CoolObjectEventArgs>(mCoolObject_CoolEvent), sender, args); return; } // do the dirty work of my method here }
对于非事件,可以使用System.Windows.Forms.MethodInvoker委托或者System.Action。
编辑:此外,每个事件都有一个对应的EventHandler
委托,因此根本不需要重新声明一个。
回答
我一直想知道总是假设需要调用是多么昂贵...
private void OnCoolEvent(CoolObjectEventArgs e) { BeginInvoke((o,e) => /*do work here*/,this, e); }
回答
一些观察:
- 除非我们是2.0之前的版本,否则不要在这样的代码中显式创建简单的委托,因此可以使用:
BeginInvoke(new EventHandler<CoolObjectEventArgs>(mCoolObject_CoolEvent), sender, args);
- 另外,我们不需要创建和填充对象数组,因为args参数是" params"类型,因此我们只需传递列表即可。
- 我可能更喜欢
Invoke
而不是BeginInvoke
,因为后者将导致异步调用代码,这可能不是我们所追求的,但是如果不调用EndInvoke,将使后续异常难以传播。将会发生的事情是,应用最终将获得" TargetInvocationException"。
回答
我们可以尝试开发某种通用组件,该组件接受SynchronizationContext作为输入并使用它来调用事件。
回答
有趣的是,WPF的绑定会自动处理封送处理,因此我们可以将UI绑定到在后台线程上修改的对象属性,而无需执行任何特殊操作。事实证明,这对我来说是一个很好的节省时间的方法。
在XAML中:
<TextBox Text="{Binding Path=Name}"/>
回答
我在网上有一些代码。比其他建议要好得多。一定要检查出来。
用法示例:
private void mCoolObject_CoolEvent(object sender, CoolObjectEventArgs args) { // You could use "() =>" in place of "delegate"; it's a style choice. this.Invoke(delegate { // Do the dirty work of my method here. }); }
回答
我认为最干净的方法肯定是走AOP路线。做几个方面,添加必要的属性,我们不必再次检查线程亲和力。