2013-04-11 59 views
1

考慮以下情況:在條形碼掃描器下掃描訂單,將訂單號發送到Windows服務應用程序,該應用程序將計算併發回價格。它將折扣券考慮在內,並且折扣在單獨的程序集中處理。 我想要做的是能夠在運行時卸載程序集,以便可以在不停止服務的情況下更換DLL。 (該服務需要30分鐘才能啓動) 所以我提出了創建一個新的AppDomain的想法,該AppDomain將加載程序集並在其中執行代碼。通信是通過命名管道和序列化完成的。 在功能上它運行良好,但生產性能非常低。有人對如何使代碼儘快運行有什麼建議嗎?AppDomain +命名管道序列化性能

代碼說明: 對於每個有折扣的訂單,都會調用DoAction。它首先啓動一個作爲命名管道客戶端的線程。該線程收到發回客戶端的價格。 然後,一個新的AppDomain被加載,如果它沒有,並且AppDomainCallback在該AppDomain的上下文中被執行。在那裏啓動一個命名管道服務器,當客戶端連接並且結果被反序列化回客戶端線程並從DoAction返回時,加載並調用包含折扣代碼的程序集。所以這裏有很多線程正在等待和序列化,但我沒有看到讓它更快的方法。

[Serializable] 
internal class ActionLoader 
{ 
    private const string DOMAINNAME = "Actions"; 

    private const string PIPE_TO = "PipeTo"; 
    private const string PIPE_BACK = "PipeBack"; 

    private string assemblyName; 
    private string className; 
    private string methodName; 
    private List<object> parameters; 

    private static BinaryFormatter formatter = new BinaryFormatter(); 

    public ActionLoader(string assemblyName, string className, string methodName, List<object> parameters) 
    { 
     this.assemblyName = assemblyName; 
     this.className = className; 
     this.methodName = methodName; 
     this.parameters = parameters; 
    } 

    private static AppDomain domain = AppDomain.CreateDomain(DOMAINNAME); 

    public OrderPrice DoAction() 
    { 
     // after clientThread is done it fills RetVal 
     ThreadedExecuter<OrderPrice> clientThread = new ThreadedExecuter<OrderPrice>(NamedPipeClient, parameters); 
     clientThread.Start(); 

     if (domain == null) // domain can be unloaded by ropsrefresh so check if it should be created again 
     { 
      domain = AppDomain.CreateDomain(DOMAINNAME); 
     } 
     // AppDomainCallback runs in the context of appdomain so dll's get loaded in there and not in CurrentDomain 
     domain.DoCallBack(AppDomainCallback); 

     clientThread.Thread.Join(); 

     return clientThread.RetVal; // return price deseralized from AppDomain 
    } 

    public static void UnloadAppDomain() // called by ropsrefresh => refresh config 
    { 
     if (domain != null) 
     { 
      AppDomain.Unload(domain); 
      domain = null; 
     } 
    } 

    private void AppDomainCallback() 
    { 
     OrderPrice price = null; 

     Assembly assembly = Assembly.LoadFrom(assemblyName); 

     object action = assembly.CreateInstance(className); 
     MethodInfo mi = action.GetType().GetMethod(methodName); 

     // using pipes to communicate between AppDomains 
     using (NamedPipeServerStream stream = new NamedPipeServerStream(PIPE_TO)) 
     { 
      stream.WaitForConnection(); 

      List<object> parameters = (List<object>)DeserializeFromStream(stream); 

      Type t = action.GetType(); 

      if (mi != null) 
       price = (OrderPrice)mi.Invoke(action, parameters.ToArray()); 
     } 

     // server becomes client to serialize data back 
     using (NamedPipeClientStream stream = new NamedPipeClientStream(PIPE_BACK)) 
     { 
      stream.Connect(); 
      SerializeToStream(stream, price); 
     } 
    } 

    private static OrderPrice NamedPipeClient(object parameters) 
    { 
     OrderPrice price = null; 

     // using pipes to communicate between AppDomains 
     using (NamedPipeClientStream stream = new NamedPipeClientStream(PIPE_TO)) 
     { 
      stream.Connect(); 
      SerializeToStream(stream, parameters); // serialize function parameters to pipe stream 
     } 

     using (NamedPipeServerStream stream = new NamedPipeServerStream(PIPE_BACK)) 
     { 
      stream.WaitForConnection(); 

      price = (OrderPrice)DeserializeFromStream(stream); 
     } 

     return price; // returns deserialized price to ThreadedExecutor 
    } 

    private static object DeserializeFromStream(Stream stream) 
    { 
     return formatter.Deserialize(stream); 
    } 

    private static void SerializeToStream(Stream stream, object parameters) 
    {    
     formatter.Serialize(stream, parameters); 
    } 
} 

回答

1

重寫了代碼以使用任務庫。由於它使用Windows ThreadPool,因此性能更好。 task.Result會阻塞直到任務完成,因此不再需要連接。

public OrderPrice DoAction() 
{ 
    Task<OrderPrice> task = Task<OrderPrice>.Factory.StartNew(NamedPipeClient); 

    if (domain == null) 
     domain = AppDomain.CreateDomain(DOMAINNAME); 
    domain.DoCallBack(AppDomainCallback); 

    return task.Result; 
}