我最近使用了另一种可能比信号量方法更简单的方法,只需在程序集中定义一个两个 appdomain 都可以引用的接口。然后创建一个实现该接口并从 MarshalByRefObject 派生的类
接口可以是任意的,请注意,当调用越过 appdomain 边界时,接口中任何方法的任何参数都必须被序列化
/// <summary>
/// An interface that the RealtimeRunner can use to notify a hosting service that it has failed
/// </summary>
public interface IFailureNotifier
{
/// <summary>
/// Notify the owner of a failure
/// </summary>
void NotifyOfFailure();
}
然后在父 appdomain 可以使用的程序集中,我定义了从 MarshalByRefObject 派生的该接口的实现:
/// <summary>
/// Proxy used to get a call from the child appdomain into this appdomain
/// </summary>
public sealed class FailureNotifier: MarshalByRefObject, IFailureNotifier
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
#region IFailureNotifier Members
public void NotifyOfFailure()
{
Log.Warn("Received NotifyOfFailure in RTPService");
// Must call from threadpool thread, because the PerformMessageAction unloads the appdomain that called us, the thread would get aborted at the unload call if we called it directly
Task.Factory.StartNew(() => {Processor.RtpProcessor.PerformMessageAction(ProcessorMessagingActions.Restart, null);});
}
#endregion
}
因此,当我创建子 appdomain 时,我只需将 new FailureNotifier() 的实例传递给它。由于 MarshalByRefObject 是在父域中创建的,因此对其方法的任何调用都将自动编组到创建它的 appdomain 中,而不管它是从哪个 appdomain 调用的。由于调用将从另一个线程发生,因此无论接口方法做什么都需要是线程安全的
_runner = RealtimeRunner.CreateInNewThreadAndAppDomain(
operationalRange,
_rootElement.Identifier,
Settings.Environment,
new FailureNotifier());
...
/// <summary>
/// Create a new realtime processor, it loads in a background thread/appdomain
/// After calling this the RealtimeRunner will automatically do an initial run and then enter and event loop waiting for events
/// </summary>
/// <param name="flowdayRange"></param>
/// <param name="rootElement"></param>
/// <param name="environment"></param>
/// <returns></returns>
public static RealtimeRunner CreateInNewThreadAndAppDomain(
DateTimeRange flowdayRange,
byte rootElement,
ApplicationServerMode environment,
IFailureNotifier failureNotifier)
{
string runnerName = string.Format("RealtimeRunner_{0}_{1}_{2}", flowdayRange.StartDateTime.ToShortDateString(), rootElement, environment);
// Create the AppDomain and MarshalByRefObject
var appDomainSetup = new AppDomainSetup()
{
ApplicationName = runnerName,
ShadowCopyFiles = "false",
ApplicationBase = Environment.CurrentDirectory,
};
var calcAppDomain = AppDomain.CreateDomain(
runnerName,
null,
appDomainSetup,
new PermissionSet(PermissionState.Unrestricted));
var runnerProxy = (RealtimeRunner)calcAppDomain.CreateInstanceAndUnwrap(
typeof(RealtimeRunner).Assembly.FullName,
typeof(RealtimeRunner).FullName,
false,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new object[] { flowdayRange, rootElement, environment, failureNotifier },
null,
null);
Thread runnerThread = new Thread(runnerProxy.BootStrapLoader)
{
Name = runnerName,
IsBackground = false
};
runnerThread.Start();
return runnerProxy;
}