在UI线程上调用一个方法,以及神秘的System.Windows.Forms.MethodInvoker.Invoke()

时间:2014-05-22 13:18:09

标签: .net multithreading delegates invoke ui-thread

我试图通过在主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()方法?我错过了什么?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

创建将运行线程的类。创建一个可以传递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);
             }
          }
        });
   }

同时在用户界面形式上取消了:

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

这有点粗糙,SO上有其他答案可以调用调用的最佳做法。