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

时间:2008-11-06 19:40:48

标签: c# multithreading

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

6 个答案:

答案 0 :(得分:26)

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

例如:

public class ClassCreatedBySomeThread
{
    Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 

    public void SafelyCallMeFromAnyThread(Action a)
    {
       dispatcher.Invoke(a);
    }
} 

答案 1 :(得分:12)

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

我的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>

答案 2 :(得分:11)

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

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

答案 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(...);

答案 4 :(得分:2)

线程不能只在另一个线程上执行。你可以得到的最接近的是将一个委托放在一个队列上,以便另一个线程执行,但这假设另一个线程正在合作这个。

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

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

答案 5 :(得分:2)

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

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

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