2011-03-30 41 views
6

我有一個服務加載一個子AppDomain,然後啓動一個運行在其中的線程。它需要一個AppDomain,因爲它動態生成並加載一些代碼,我需要能夠重新啓動它而不會中止整個服務。如何正常卸載有線程運行的子AppDomain

所以有一個線程運行在子AppDomain的事件循環中,它通過一個MarshalByRefObject傳遞給它的事件傳遞給它,這個事件將一些東西放在併發隊列中。我想停止並卸載AppDomain子項並創建一個新的子項。

我可以簡單地在子AppDomain上調用Unload,但會中止所有線程並拋出ThrearAbortException。我如何優雅地關閉它?如果我使用MarshalByRefObject在子AppDomain中設置了一些靜態標誌,那麼主進程將如何等待完成卸載?

我有一些示例代碼,它顯示了它的設置以及如何調用Unload來殺死它,我怎麼修改這個以允許優雅的卸載並且永遠不會有多個子AppDomain?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security; 
using System.Security.Permissions; 
using System.Reflection; 
using System.Threading; 

namespace TestAppDomains 
{ 
    /// <summary> 
    /// Calls to methods magically get transfered to the appdomain it was created in because it derives from MarshalByRefObject 
    /// </summary> 
    class MarshalProxy : MarshalByRefObject 
    { 
     public AppDomain GetProxyAppDomain() 
     { 
      return AppDomain.CurrentDomain; 
     } 

     public void SayHello() 
     { 
      Console.WriteLine("MarshalProxy in AD: {0}", AppDomain.CurrentDomain.FriendlyName); 
     } 

     public void RunLoop() 
     { 
      try 
      { 
       while (true) 
       { 
        Console.WriteLine("RunLoop {0} in {1}", DateTime.Now.ToLongTimeString(), AppDomain.CurrentDomain.FriendlyName); 
        Thread.Sleep(1000); 
       } 
      } 
      catch(Exception ex) 
      { 
       Console.WriteLine("You killed me! {0}", ex); 
       Thread.Sleep(200); //just to make sure the unload is really blocking until its done unloading 
       // if the sleep is set to 2000 then you will get a CannotUnloadAppDomainException, Error while unloading appdomain. (Exception from HRESULT: 0x80131015) thrown from the .Unload call 
      } 
     } 

     static int creationCount = 1; 
     public static MarshalProxy RunInNewthreadAndAppDomain() 
     { 
      // Create the AppDomain and MarshalByRefObject 
      var appDomainSetup = new AppDomainSetup() 
      { 
       ApplicationName = "Child AD", 
       ShadowCopyFiles = "false", 
       ApplicationBase = Environment.CurrentDirectory, 
      }; 

      var childAppDomain = AppDomain.CreateDomain(
       "Child AD " + creationCount++, 
       null, 
       appDomainSetup, 
       new PermissionSet(PermissionState.Unrestricted)); 

      var proxy = (MarshalProxy)childAppDomain.CreateInstanceAndUnwrap(
       typeof(MarshalProxy).Assembly.FullName, 
       typeof(MarshalProxy).FullName, 
       false, 
       BindingFlags.Public | BindingFlags.Instance, 
       null, 
       new object[] { }, 
       null, 
       null); 

      Thread runnerThread = new Thread(proxy.RunLoop); 
      runnerThread.Name = "MarshalProxy RunLoop"; 
      runnerThread.IsBackground = false; 
      runnerThread.Start(); 

      return proxy; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("I am running in AD: {0}", AppDomain.CurrentDomain.FriendlyName); 

      var proxy = MarshalProxy.RunInNewthreadAndAppDomain(); 
      proxy.SayHello(); 

      while (true) 
      { 
       Console.WriteLine("Press enter to kill and restart proxy"); 
       Console.WriteLine(); 
       Console.ReadLine(); 

       Console.WriteLine("Unloading"); 
       AppDomain.Unload(proxy.GetProxyAppDomain()); 
       Console.WriteLine("Done unloading"); 

       proxy = MarshalProxy.RunInNewthreadAndAppDomain(); 
      } 
     } 
    } 
} 
+2

首先優雅地停止線程。 – 2011-03-31 00:35:18

回答

8

請嘗試以下

runnerThread.IsBackground = true; 

而且,是的,還有的AppDomain沒有優美的卸載,如果你不先停止線程。

2

這種情況基本上與兩個AppDomain是分開的進程相同,因此您需要使用某種形式的IPC。一種選擇是在要求循環停止時將事件句柄傳遞給子AppDomain。循環可以在退出之前發信號通知事件。等待事件給循環一些時間完成。如果你超時,那麼你可以做一個粗略的卸載。