考虑以下情况:在条形码扫描仪下扫描订单,订单号被发送到 Windows 服务应用程序,该应用程序将计算并发送回价格。除其他事项外,它还考虑了折扣券,并且折扣在单独的程序集中处理。我想要做的是能够在运行时卸载程序集,以便可以替换 dll 而不必停止服务。(该服务需要 30 分钟才能启动)所以我想出了创建一个新的 AppDomain 的想法,该 AppDomain 将加载程序集并在其中执行代码。通信是通过命名管道和序列化完成的。在功能上它运行良好,但在生产中性能变得非常低。有人对如何使下面的代码尽可能快地运行有任何建议吗?
代码说明:对于每个有折扣的订单,都会调用 DoAction。它首先启动一个充当命名管道客户端的线程。该线程接收发送回客户端的价格。然后,如果尚未加载新的 AppDomain,则在该 AppDomain 的上下文中执行 AppDomainCallback。命名管道服务器在那里启动,包含折扣代码的程序集在客户端连接时被加载和调用,结果被反序列化回客户端线程并从 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);
}
}