在调度程序线程外部使用Application.Current.Dispatcher处理事件的方法

4
问题:在引发事件的工作线程中,还是在处理事件的UI代码中应该放置 "Application.Current.Dispatcher.Invoke"?两种方式都可以正常工作,但哪种更好的做法或有什么区别?
在创建新闻滚动条的WPF应用程序中,我通过Microsoft.Office.Interop.Outlook和ItemAdd事件从新收件箱电子邮件中检索正文文本,然后引发事件传递正文文本。主视图模型如下订阅此事件。在OutlookReader类中:
private void OnEmailFoundWithSubjectMatchingFilter(_MailItem item)
{
    System.Windows.Application.Current.Dispatcher.Invoke(() =>
    {
        FoundEmailWithSubjectMatchingFilter?.Invoke(this, item.Body);
    });
}

而在MainViewModel类中:

private void HandleEmailFeed(object sender, string e)
{
    //Application.Current.Dispatcher.Invoke(() =>
    //{
    var parser = new MailBodyParser();

    AddFeedItem(parser.Parse(e));
    //});
}

没有一个正确的答案。做任何与库的用户期望更一致的事情。有时这被称为最小惊讶原则。 - Mike Zboray
我倾向于认为UI端应该调用分发器,因为工作线程不应该真正负责结果的使用方式。我真的很想知道是否有任何技术/性能差异。 - JJJulien
1
我不明白你在问什么。按照惯例,“UI线程”是指调度程序线程。如果您已经在UI线程中执行代码,则无需调用“Dispatcher.Invoke()”。您只需要从其他线程而非UI线程调用“Dispatcher.Invoke()”。 - Peter Duniho
编写多线程代码从来都不容易,它有一种强烈的倾向,在你不留意的时候就会出现故障。而且这些故障模式几乎不可能被诊断出来。这就使得不知道你的代码运行在哪个线程上成为一个巨大的代码坏味。如果你不知道,那么你永远无法证明你的代码是线程安全的。绝对不要这样做。 - Hans Passant
如果您担心在不需要时使用调度程序会带来性能损失,您可以使用CheckAccess方法:if (!Application.Current.Dispatcher.CheckAccess()) { // You should use invoke } else { // Invoke isn't required } https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess(v=vs.110).aspx - Kevin Gosse
@ Peter Duniho:感谢您对术语的澄清。我已经修改了问题以使其更加清晰。我是C#的新手,正在尝试理解Dispatcher调用应该在哪里使用:在引发事件的代码中(在这种情况下是运行Outlook的线程)还是在MainViewModel代码中(调度程序线程),或者是否没有区别? - JJJulien
1个回答

0
"应该将“Application.Current.Dispatcher.Invoke”放在引发事件的工作线程中还是放在处理事件的 UI 代码中?"
我会说后者。主要是因为工作线程可能在没有任何 System.Windows.Application 对象的类库中启动。
因此,工作线程应该只触发事件,然后客户端应用程序负责仅在它们最初创建的调度程序线程上更新 UI 元素。类库不需要关心或甚至知道任何调度程序。

2
不确定为什么这个回答被投票否决了,但是是的,这将是答案。这是我在SO上的第一个问题,正如Peter Duniho所提到的,我本可以措辞更好。直到我发帖后才意识到我真正想要回答的问题是:在WPF中,处理工作线程和调度线程之间事件的最佳模式/实践是什么?那也可能太过开放,但我会加以完善并发布新问题。感谢您的回复。 - JJJulien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接