I have a process that can have multiple AppDomains. Each AppDomain collect some statistics. After a specified time, I want to accumulate these statistic and save them into a file.
One way to do this is Remoting, which I want to avoid.
The only other technique I have in mind is to save each AppDomain's data in a file, and after a specific time, one of the AppDomain collects all data and accumulate them.
But it would be ideal if this all could be done in-memory, without the cost of serializing the information to pass between AppDomains. Anyone have any ideas?
It is possible to share data between AppDomains without the costs of Marshalling. But it is a rather hacky way. You can create a source data object which is shared by reference between all AppDomains. This way you get all data into one shared object without the costs of Marshalling. Sounds too easy to be true?
The first thing is to know how share data between AppDomains without Marshalling. For this you get the object address of your data source object via Marshal.UnsafeAddrOfPinnedArrayElement. Then you pass this IntPtr to all AppDomains which are interested in this. In the target AppDomain you need to cast this IntPtr back to an object reference which can be done JIT::CastAny which is done if you return an object from a method and push the pointer of it onto the stack.
Viola you have shared an object as plain pointer between AppDomains and you get InvalidCastExceptions. The problem is that you must set for all your AppDomains LoaderOptimization.MultiDomain to ensure that the assembly that defines the shared data type is loaded as AppDomain neutral type which has the same Method Table pointer between all AppDomains.
You can find an example application that does exactly this as part of WMemoryProfiler. See this link for a more detailed explanation and download link to the sample code.
The basic code is
[LoaderOptimization(LoaderOptimization.MultiDomain)]
static public void Main(string[] args)
{
// To load our assembly appdomain neutral we need to use MultiDomain on our hosting and child domain
// If not we would get different Method tables for the same types which would result in InvalidCastExceptions
// for the same type.
var other = AppDomain.CreateDomain("Test"+i.ToString(), AppDomain.CurrentDomain.Evidence, new AppDomainSetup
{
LoaderOptimization = LoaderOptimization.MultiDomain,
});
// Create gate object in other appdomain
DomainGate gate = (DomainGate)other.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(DomainGate).FullName);
// now lets create some data
CrossDomainData data = new CrossDomainData();
data.Input = Enumerable.Range(0, 10).ToList();
// process it in other AppDomain
DomainGate.Send(gate, data);
// Display result calculated in other AppDomain
Console.WriteLine("Calculation in other AppDomain got: {0}", data.Aggregate);
}
}