有没有任何方法可以让事件处理程序直接在UI线程上运行,而不需要中间线程池线程

本文关键字:线程 运行 不需要 中间线 中间 UI 方法 任何 事件处理 程序 有没有 | 更新日期: 2023-09-27 18:00:54

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

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

有没有任何方法可以重新构造它,以避免创建这个寿命很短的中间线程池线程(即处理程序代码直接在UI线程上运行(?

我不希望事件以同步方式(Invoke(启动(它在各种上下文中使用(,而且它不应该被阻塞。

有没有任何方法可以让事件处理程序直接在UI线程上运行,而不需要中间线程池线程

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

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

如果您确实控制了触发事件的代码,那么您可以选择确保事件处理程序都在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的属性,或者将代码编组到给定上下文的其他机制(您可以发明自己的、使用委托等(