【问题标题】:Invoking a method on the UI thread, and the mysterious System.Windows.Forms.MethodInvoker.Invoke()在 UI 线程上调用方法,以及神秘的 System.Windows.Forms.MethodInvoker.Invoke()
【发布时间】:2014-05-22 13:18:08
【问题描述】:

我正在尝试通过在主 UI 线程上调用工作线程来运行代码;但是,我没有主窗体或任何控件的实例(我也不希望在线程代码正在运行的类中有一个)。

我发现了诸如内置 System.Windows.Forms.MethodInvoker 委托之类的东西,它有一个 Invoke() 方法。我想如果我在主线程上实例化我的MethodInvoker,然后在工作线程上调用它的Invoke() 方法,一切都会按我的意愿工作。但是,简单的调试表明被调用的方法仍然在工作线程上运行。

我在MethodInvoker.Invoke() 上找不到任何文档。 MSDN 上甚至没有“会员”链接,但它确实存在。我发现的唯一一件事,即我已经知道的,是这个 SO 帖子中接受的答案:

Using C# MethodInvoker.Invoke() for a GUI app... is this good?

所以,我的问题是:

  1. 如何在没有主窗体实例或其他 UI 元素的情况下调用主线程上的方法?

  2. 为什么没有在任何地方记录 MethodInvoker.Invoke() 方法?我错过了什么?

【问题讨论】:

  • 您可以将invoke方法传递给在启动期间运行线程的代码,以便您可以调用它。通常,当我从后台线程获得 UI 需要的信息时,我会发送 UI 的某些部分正在侦听的通知事件,然后让事件处理程序调用调用。如果这对您的代码不可行,您能解释一下吗?
  • MethodInvoker 是一个委托,这意味着它定义了一个函数签名,但没有行为。它是传递给类的 Invoke 方法的内容(在本例中为您的 UI 表单)
  • @SteveMitcham -- 谢谢。我有一个带有计时器的类,它负责在计时器经过时更新多个 UI 元素。此类实际上并不包含对这些 UI 元素的引用;相反,它包含对他们“代码背后”的引用(我将表示与逻辑分开)。我不想存储任何 UI 元素,也不想让我的计时器类对 UI 有任何了解。
  • @SteveMitcham -- 我喜欢你关于在 UI 中使用事件处理程序的建议。

标签: .net multithreading delegates invoke ui-thread


【解决方案1】:

为什么没有在任何地方记录 MethodInvoker.Invoke() 方法?我错过了什么?

已记录在案。它由 C# 编译器为每个声明的 System.DelegateBeginInvoke()/EndInvoke() 生成。您很少(或从不)必须直接调用它,因为您只需使用函数式调用:

MethodInvoker func = ...
func(this, EventArgs.Empty);

此外,请注意MethodInvoker 只是一个委托而不是一个将调用分派给其他线程的类。这是什么意思?如果您调用它,那么它将在调用者线程中被调用。

如何在没有主窗体实例或其他 UI 元素的情况下调用主线程上的方法?

只是你不能。幸运的是,您可以访问System.Windows.Forms.Application.OpenForms 列表。选择第一个打开的表单并使用它将调用调度到 UI 线程。

if (Application.OpenForms.Count > 0)
    Application.OpenForms.BeginInvoke(...);
else
    // Ooops, no UI? You may invoke directly

【讨论】:

    【解决方案2】:

    创建将在线程外运行的类时。创建一个事件,可以将通知数据传递出去,UI 对象可以挂钩,然后 UI 可以根据需要调用 invoke。

    class MyBackgroundWorker
    {
       public event EventHandler<MyArgs> Progress;
    
       public void Start()
       {
            Task.Factory.StartNew(() => {
              while(shouldBeDoingStuff) {
                 ...  stuff to be done ...
                 if (Progress) 
                 {
                    Progress(myArgs);
                 }
              }
            });
       }
    

    同时在启动事件的 UI 表单上:

       var task = new MyBackgroundWorker();
       task.Progress += (myArgs) => {
           Invoke(()=> HandleEvent(myArgs));
       }
    

    这有点粗略,关于调用调用的最佳实践还有其他关于 SO 的答案。

    【讨论】:

      猜你喜欢
      • 2019-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多