【问题标题】:How to invoke a function on parent thread in .NET?如何在.NET 中调用父线程上的函数?
【发布时间】:2008-11-06 19:40:47
【问题描述】:

我有一个 .NET 类库,其中包含一个具有执行一些冗长操作的方法的类。当客户端调用此方法时,它应该在新线程上执行冗长的操作,以避免阻塞调用者。但是一旦方法完成,它应该在主线程上执行一些代码。在 WinForms 应用程序中,我可以使用 System.Windows.Forms.Control.Invoke 方法,但这不是我的情况。那么如何在 C# 中实现这一点呢?

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    您可以使用System.Windows.Threading.Dispatcher 对象(来自WindowsBase 程序集)在特定线程上调用函数。

    例如:

    public class ClassCreatedBySomeThread
    {
        Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 
    
        public void SafelyCallMeFromAnyThread(Action a)
        {
           dispatcher.Invoke(a);
        }
    } 
    

    【讨论】:

      【解决方案2】:

      我找到了一个简单的解决方案:

      我的 COM 对象是这样声明的:

      public class Runner
      {
          public void Run(string executable, object processExitHandler)
          {
              ThreadPool.QueueUserWorkItem(state =>
              {
                  var p = new Process()
                  {
                      StartInfo = new ProcessStartInfo()
                      {
                          FileName = executable
                      }
                  };
                  p.Start();
                  while (!p.HasExited)
                  {
                      Thread.Sleep(100);
                  }
      
                  state
                      .GetType()
                      .InvokeMember(
                          "call", 
                          BindingFlags.InvokeMethod, 
                          null, 
                          state, 
                          new object[] { null, p.ExitCode }
                      );
              }, processExitHandler);
          }
      }
      

      在我的 HTML 页面中,我这样使用它:

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
      <html>
      <head><title>ActiveXRunner</title>    
          <script type="text/javascript">
          function runNotepad() {
              var ax = new ActiveXObject('ActiveXRunner.Runner');
              ax.Run('c:\\windows\\notepad.exe', h);
          }
      
          function h(exitCode) {
              alert('exitCode = ' + exitCode);
          }
          </script>
      </head>
      <body>
          <a href="#" onclick="runNotepad();">Run notepad and show exit code when finished</a>
      </body>
      </html>
      

      【讨论】:

        【解决方案3】:

        如果一个线程必须能够执行由另一个线程发布给它的一些代码(通常以委托的形式),它基本上必须等待这些指令。你的主线程还在做什么?构建一个等效的事件循环并不难(基本上你会有一个生产者/消费者代表队列),但你需要知道你不能只是中断主线程并说“现在就做”。

        为什么必须在主线程上执行?

        【讨论】:

        • 如何使用调度程序(请参阅下面的答案)。至于为什么这可能是必要的,在处理调用遗留代码的远程接口时,我不得不做这样的事情。
        【解决方案4】:

        没有办法显式地使代码在特定线程上运行(用于创建 UI 控件的线程除外 - 这是一个例外),但如果您只是想在线程完成时调用代码以外的代码, 你使用委托。
        首先声明一个委托,带有你想在新线程上运行的方法的签名......

        public delegate bool CheckPrimeDelegate(long n);
        

        然后在您的代码中,创建一个委托实例,使用 BeginInvoke 调用它(传递它需要的任何参数)并传递一个回调函数委托 (OnChkPrimeDone)

        class MyClassApp
        {
           static void Main() 
           {
              CheckPrimeDelegate ckPrimDel = new CheckPrimeDelegate(Prime.Check);
        
              // Initiate the operation
              ckPrimDel.BeginInvoke(4501232117, new AsyncCallback(OnChkPrimeDone), null);
        
              // go do something else . . . .      
           }
        
           static void OnChkPrimeDone( IAsyncResult iAr)
           {
                AsyncResult ar = iAr as AsynchResult;
                 CheckPrimeDelegate ckPrimDel = ar.AsyncDelegate as CheckPrimeDelegate;
                 bool isPrime = ckPrimDel.EndInvoke(ar);
                 Console.WriteLine(" Number is " + (isPrime? "prime ": "not prime");
           }
        }
        

        完成后会调用回调函数(OnChkPrimeDone)

        如果您明确需要在用于创建 COM Active-X 对象的线程上运行此回调函数,请检查保存对该对象的引用的 .Net 托管代码包装器变量...如果它有调用 InvokeRequired() 方法,然后在您的回调函数中测试此方法的布尔返回值。
        如果它有 InvokeRequired() 方法并且它返回 true,那么 active-X 对象也将公开一个“BeginInvoke()”方法。然后,创建另一个委托,填充相同的函数,并调用 BeginInvoke 在 Active-X 对象上,将这个新委托传递给它...然后它将在用于创建 Active-X 对象的同一线程上运行

        If (MyActiveXObject.InvokeRequired())
             MyActiveXObject.BeginInvoke(...);
        

        【讨论】:

        • “没有办法显式地使代码在特定线程上运行”,您可以使用 Dispatcher 对象。这使得这样做相对容易。
        【解决方案5】:

        一个线程不能只在另一个线程上执行东西。您可以得到的最接近的方法是将委托放在队列中以供其他线程执行,但这假定其他线程正在为此进行合作。

        在 WinForms 应用程序中,主循环在每次循环迭代时查找此类排队消息。

        如果您只需要传达工作线程的完成,您可以使用例如一个标志变量。如果主线程应该能够等待作业终止,请使用信号量或条件变量(监视器)。

        【讨论】:

          【解决方案6】:

          这是我的确切场景:我有一个作为 COM 对象公开的 .NET 类库(使用 regasm.exe)。 COM 对象包含一个运行外部应用程序的方法(使用 Process.Start)。 COM 对象用于 Internet Explorer。所以我的网页运行外部应用程序,我需要找到一种方法将 ExitCode 传递给网页。

          起初我没有在新线程上启动外部应用程序,只是等待用户关闭应用程序,然后我的函数将 ExitCode 返回给调用者。但是,当应用程序运行时,IE 没有响应。所以我决定在一个新线程中启动应用程序,但现在我无法再返回 ExitCode。

          COM 对象是使用:new ActiveXObject 指令创建的,不幸的是 javascript 不支持事件接收,因此我无法在 C# 中编写应用程序退出时触发的事件。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-07-19
            • 1970-01-01
            • 1970-01-01
            • 2011-04-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多