在.NET中在主UI线程上触发事件。

14 浏览
0 Comments

在.NET中在主UI线程上触发事件。

我正在开发一个在.NET中其他开发人员最终会使用的类库。这个类库使用了几个工作线程,这些线程会触发状态事件,导致一些UI控件在WinForms / WPF应用程序中更新

通常情况下,对于每次更新,您需要检查WinForms的.InvokeRequired属性或等效的WPF属性,并在主UI线程上调用它进行更新。这样做可能会很繁琐,而且让最终开发人员这样做并不合适,所以...

是否有办法让我的类库从主UI线程触发/调用事件/委托?

特别是...

  1. 我是否应该自动"检测"要使用的"主"线程?
  2. 如果不是,我是否应该要求最终开发人员在应用程序启动时调用某个(伪)UseThisThreadForEvents()方法,以便我可以从该调用中获取目标线程?
0
0 Comments

在.NET中,如果在主UI线程上引发事件,可能会出现问题。解决这个问题的方法是使用SynchronizationContext类来将调用传递到WinForms或WPF中的UI线程,可以使用SynchronizationContext.Current来实现。

SynchronizationContext类是一个用于跨线程同步的基类,它提供了一个上下文,可以在不同线程之间传递消息。在WinForms或WPF应用程序中,可以使用SynchronizationContext类来确保事件在主UI线程上引发。

下面是一个示例代码,演示了如何使用SynchronizationContext类来引发事件在主UI线程上:

// 获取当前的SynchronizationContext
SynchronizationContext context = SynchronizationContext.Current;
// 在后台线程中引发事件
void WorkerThread()
{
    // 引发事件
    context.Post(new SendOrPostCallback(OnEventRaised), null);
}
// 在主UI线程上处理事件
void OnEventRaised(object state)
{
    // 处理事件
}
// 启动后台线程
Thread workerThread = new Thread(WorkerThread);
workerThread.Start();

在上面的代码中,我们首先使用SynchronizationContext.Current获取当前的SynchronizationContext对象。然后,在后台线程中使用该对象的Post方法来引发事件。这将确保事件在主UI线程上处理。

通过使用SynchronizationContext类,我们可以解决在主UI线程上引发事件的问题。这样做可以确保事件处理在UI线程上执行,避免了可能出现的线程冲突和UI更新问题。

0
0 Comments

问题的出现原因是在.NET中,当使用多线程时,如果在非主UI线程上触发事件,可能会导致线程安全问题。解决方法是在主UI线程上触发事件。

为了解决这个问题,可以使用扩展方法来实现在主UI线程上触发事件。这个扩展方法的名称是`Raise`,它接受一个`MulticastDelegate`类型的参数,表示要触发的事件。它还接受一个`sender`参数和一个`EventArgs`参数,分别表示事件的源和事件数据。该方法返回事件调用的返回值,如果没有返回值,则返回null。

在这个扩展方法内部,首先将传入的`multicastDelegate`参数赋值给一个线程安全的`threadSafeMulticastDelegate`变量。然后,使用`GetInvocationList`方法获取`threadSafeMulticastDelegate`中所有委托的列表,并遍历每一个委托。

对于每一个委托,首先判断它的`Target`是否实现了`ISynchronizeInvoke`接口,并且是否需要调用委托的线程与当前线程不同。如果是这样,就使用`BeginInvoke`方法在委托的目标线程上异步调用委托,并使用`EndInvoke`方法获取异步调用的返回值。否则,直接使用`DynamicInvoke`方法调用委托。

最后,返回事件调用的返回值。

使用这个扩展方法,可以在任何地方触发事件,而无需担心线程安全问题。只需要调用`Raise`方法,并传入事件的源和事件数据作为参数。

在这个的问题中,还提到了为什么要创建`threadSafeMulticastDelegate`的副本。根据讨论的内容,这是为了避免在遍历委托列表时,如果有其他线程同时修改了`multicastDelegate`,可能会导致异常。因此,创建一个副本可以确保在遍历期间委托列表的一致性。

此外,如果使用自定义的事件参数,也可以使用泛型版本的`Raise`方法来触发事件,只需要在方法名后面加上``,并在参数列表中添加一个泛型类型约束。

总之,通过在主UI线程上触发事件,可以避免在多线程环境下可能出现的线程安全问题。使用扩展方法`Raise`可以简化事件触发的代码,并提高代码的可读性和可维护性。

0
0 Comments

在.NET中,当在主UI线程上引发事件时,会出现以下问题:

问题的原因是在事件的调用列表中,目标委托的目标线程是ISynchronizeInvoke时,库可以检查每个委托的目标,并将调用传递给目标线程:

解决方法之一是在你的库中要求客户端传递一个ISynchronizeInvoke或SynchronizationContext,以便他们可以指定在哪个线程上引发事件。这样,库的用户比"秘密检查委托目标"的方法更具可见性和控制性。

另一种方法是在用户代码调用可能导致事件触发的OnXxx或其他API中,将线程调度的代码放在其中。

以下是一个示例代码,演示了如何在主UI线程上引发事件:

private void RaiseEventOnUIThread(Delegate theEvent, object[] args)
{
  foreach (Delegate d in theEvent.GetInvocationList())
  {
    ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
    if (syncer == null)
    {
      d.DynamicInvoke(args);
    }
    else
    {
      syncer.BeginInvoke(d, args);
    }
  }
}

然而,这种方法可能适用于WinForms,但不适用于WPF。在WPF中,可以使用theGecko提供的解决方法。

0