【问题标题】:Is there any way to get an event handler to run directly on UI thread without intermediate threadpool thread?有没有办法让事件处理程序直接在 UI 线程上运行而无需中间线程池线程?
【发布时间】:2013-02-25 14:59:21
【问题描述】:

我有一个通过 BeginInvoke 异步触发的事件 - 因此事件处理程序拥有自己的线程池线程。但是事件处理程序中的所有代码都希望被分派到 UI 线程 - 所以整个事件处理程序代码都在 Dispatcher.BeginInvoke 块中。这意味着:

  1. 使用 BeginInvoke 启动线程触发事件。
  2. 为要运行的事件处理程序代码创建了一个新的线程池线程。
  3. 事件处理程序立即将其代码异步发布到 UI 线程,使用 调度程序(Dispatcher.BeginInvoke(...所有处理程序代码...)。
  4. 临时线程池线程准立即返回 - 创建它只是为了执行 UI 线程发布。

有没有什么方法可以重组它以避免创建这个非常短暂的中间线程池线程(即处理程序代码直接在 UI 线程上运行)?

我不希望事件同步触发(调用)- 它在各种上下文中使用- 它不应该被阻塞。

【问题讨论】:

    标签: c# wpf multithreading threadpool dispatcher


    【解决方案1】:

    你可以直接调用Dispatcher.BeginInvoke而不是使用线程池线程吗?

    【讨论】:

    • 否 - 在我调用 Dispatcher.BeginInvoke 的事件处理程序中 我已经在线程池线程中(来自异步触发的事件 myevent.BeginInvoke)。为了澄清我使用线程池线程明确指出 - 这正是事件处理程序正在运行的内容,因为事件是与 BeginInvoke 异步触发的。
    • 对,但是在第1步中,启动线程可以直接调用Dispatcher.BeginInvoke而不是启动这个线程池线程吗?
    • 启动线程在库中。它对处理程序一无所知(在一种情况下会发布到 UI,但在其他情况下则不会)。
    • 如果启动线程在库中并且它正在线程池上发出您的请求,那么您可能会不走运。除非有办法修改库的该部分或要求它以不同的方式启动事件......
    【解决方案2】:

    如果您不控制触发事件的代码,您就别无选择;在这么短的时间内无法使用线程池线程。

    如果您确实控制了触发事件的代码,您可以选择确保事件处理程序全部在 UI 线程中运行。虽然这当然是可能,但您应该仔细考虑是否应该如此。对于某些事件处理程序来说,让它们在 UI 线程中运行才有意义(例如,Form 的大多数事件在 UI 线程中运行)而有些则不然。如果您的事件在 UI 控件上,那么它在 UI 线程中运行可能是有意义的。如果这是您目前恰好从 UI 线程使用的某个工作类的事件,那么在 UI 线程中触发该事件可能是个坏主意(因为您将来可能会使用该工作上下文在非 WPF 上下文中)。

    如果您确实想在 UI 线程中触发事件,这很简单。如果您在打算触发事件时已经在 UI 线程中,只需同步调用它:

    var eventCopy = MyEvent;
    if(eventCopy != null) eventCopy();
    

    如果您希望触发事件时不在 UI 线程中,则在调用上述代码之前编组到 UI 线程:

    Dispatcher.BeginInvoke(()=>{ //Or just `Invoke`, if that's appropriate in context
        var eventCopy = MyEvent;
        if(eventCopy != null) eventCopy();
    });
    

    根据您的编辑,您似乎希望根据某些特定上下文有条件地在 UI 线程或线程池线程中触发事件,而不是始终在 UI 线程中触发。

    虽然这是可能的,但您需要决定是否值得。

    作为示例,您可以查看System.Timers.Timer,它有一个SynchronizingObject 属性,可让您确定事件是如何引发的(null 用于线程池,或者能够编组到特定上下文的对象特定 UI 模型的事件)。

    您可以遵循一般模式。

    有很多具体的方法。您可以在首次创建工作线程时捕获SynchronizationContext.Current 中的值,并使用它(如果您可能需要禁用或强制启用它,可以使用布尔值来禁用捕获源同步上下文)。

    另一种选择是只拥有一个接受SynchronizationContext 的属性,或其他一些将代码编组到给定上下文的机制(您可以自己发明、使用委托等)。

    【讨论】:

    • 这证实了我的怀疑。该事件在工作类上,总是在 UI 上调用是没有意义的(这种情况是 UI 应用程序中事件的特殊用途,但通常情况并非如此)。所以简短的回答是“在短时间内无法使用线程池”。
    • 好的 - 编辑理解 - 谢谢 - 在这一点上不值得这样做 - 但我认为你的建议是“正确”的解决方案(处理两种情况)。
    猜你喜欢
    • 2018-08-27
    • 2011-09-30
    • 1970-01-01
    • 2020-03-10
    • 2012-03-03
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多