代码之家  ›  专栏  ›  技术社区  ›  Darin Dimitrov

如何在.NET中的父线程上调用函数?

  •  23
  • Darin Dimitrov  · 技术社区  · 16 年前

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

    6 回复  |  直到 16 年前
        1
  •  26
  •   CruelCow    12 年前

    您可以使用 System.Windows.Threading.Dispatcher 对象(来自WindowsBase程序集)。

    例如:

    public class ClassCreatedBySomeThread
    {
        Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 
    
        public void SafelyCallMeFromAnyThread(Action a)
        {
           dispatcher.Invoke(a);
        }
    } 
    
        2
  •  12
  •   Darin Dimitrov    16 年前

    我找到了解决问题的简单方法:

    我的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
  •  11
  •   Jon Skeet    16 年前

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

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

        4
  •  4
  •   Mitch Wheat    16 年前

    无法显式地使代码在特定线程上运行(用于创建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()的方法,那么在回调函数中,测试该方法的布尔返回值。
    如果它有invokererequired()方法并返回true,那么active-X对象还将公开一个“BeginInvoke()”方法。然后,创建另一个用相同函数填充的委托,并调用BeginInvoke 在Active-X对象上,将此新委托传递给它。。。然后,它将在用于创建Active-X对象的同一线程上运行

    If (MyActiveXObject.InvokeRequired())
         MyActiveXObject.BeginInvoke(...);
    
        5
  •  2
  •   Bjarke Ebert    16 年前

    一个线程不能只在另一个线程上执行内容。最接近的方法是将委托放在队列上,让另一个线程执行,但这假设另一个线程正在为此进行协作。

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

        6
  •  2
  •   Darin Dimitrov    16 年前

    下面是我的具体场景:我有一个.NET类库作为COM对象公开(使用regasm.exe)。COM对象包含一个运行外部应用程序的方法(使用Process.Start)。COM对象在Internet Explorer中使用。因此,我的网页运行外部应用程序,我需要找到一种方法将ExitCode传递到网页。

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

    COM对象是通过以下方式创建的: new ActiveXObject