1

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

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

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

我不希望事件同步触发(调用)——它在各种情况下使用——它不应该被阻塞。

4

2 回答 2

4

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

于 2013-02-25T15:04:13.580 回答
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首次创建工作线程时捕获该值,并使用它(如果您可能需要禁用它或强制启用它,可能使用布尔值来禁用捕获源同步上下文)。

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

于 2013-02-25T15:15:16.540 回答