none
Best way to use AppDomians and CreateInstanceXXX RRS feed

  • Question

  • Hi

    I need some expert guidance on an aspect of .Net that I am unfamilar with.

    We have a server, it is multi-threaded and services requests for data that are described by a class-name (all classes here implement a special interface of ours).

    Our server gets a request and the name of the class (it is embedded as part of the request).

    A server thread gets the request and then creates and instance of the class and invokes a few methods on the instance, i.e. Load(), Execute() and so on, the execute actially retruns an IEnumerable.

    The server then access the IEnumerable result from Exercute and serializes the results to the client.

    It then disconnects and the thread reverts back to thread pool.

    Now this works fine and is the basis for a beta, however we have one issue, we want the classes to be rebuildable by users and want the exectuing server to pick up new versions of these as they are encountered.

    It seems as if we could devise the following pattern:

    1 If type referred to/implied by, the request has NOT changed since last request for that type then  
    2    Examine a local array of Types, for one matching the request.  
    3    If not found, then load required assembly.  
    4    Instantiate and use and instance of this to service the request.  
    5    Returns results etc in the usual way.  
    6 Else  
    7    Identify the AppDomain object (from a new array of these) associated with the required type.  
    8    Unload that AppDomain (and thus the assembly containing the type).  
    9    Create a new AppDomain that will be associated with the type.  
    10    Call CreateInstanceAndUnwrap on the AppDomain, to create an instance of the type.  
    11    Insert the new AppDomain into the local array (or some collection of sorts).  
    12    Use the created instance to service request, send results etc.  
    13  
    14  

    Is this poor thinking?

    Bear in mind the Assemblies that contain the types are NOT DLLs files, they are actually persisted binary streams and loaded dynamically - all this works fine.

    My understanding is that this approach will actually use the AppDomian to initialy create the type BUT the host AppDomian (the main part of the server) will also end up loading/getting the type's Assembly, so even though the AppDomain can be unloaded etc, the host will actually still have the Assembly loaded.

    It also seems that we can call CreateInstanceFronAndUnwrap (see Page 719 of C# 3.0 IN A NUTSHELL) and use our interface.

    However, although this will NOT cause the type's assembly to be loaded into the caller (host AppDomain) it does require a remoting operation every time we access a method on our "instance".

    Thus our 'Execute' method (which returns IEnumerable) when enumerated many thousands of times (potentially) would become much more expensive as each iteration now involves remoting back/forth.

    Is this true?

    If so are we better off making the entire thread "run" a new AppDomain so that all this takes place INSIDE the AppDomain, so we can then just unload/create new AppDomains in a similar way, but have them do more than simply "return" types.

    Finally, if I do create a new AppDomain, does it, by defintion begin to run on a separate thread all by itself?

    Thx
    • Edited by Captain Kernel Monday, February 23, 2009 8:45 AM book title
    Monday, February 23, 2009 8:42 AM

All replies

  • Hi,

    I will try to answer some of your questions, but I can't comment on the approach since I only have a couple of minutes time to think about it.

     

    1. If you use CreateInstanceFronAndUnwrap, make sure your class derives from MarshalByRefObject so that you don't load the actual type (and with it it's assembly) into your local AppDomain.
    2. If you do so, then you will get a proxy object on your local AppDomain which marshalls every method call into the remote AppDomain. This means that each method call is in fact more expensive than when you call the actual type in your local AppDomain.
      On the other hand, the overhead used for marshalling the call might be neglectable if the action inside the method is heavy. You might want to run some performance tests to determine that.
    3. In case of that the marshalling overhead is an issue for you or if the type cannot derive from MarshalByRefObject, the solution would indeed be to place all the work into the remote AppDomain and just call some helper type once to trigger the execution. 
    4. If you call a method in the remote AppDomain, that method will be called in the current thread, NOT on a separate thread. You can run the following code to verify that:
      class Program
      {
        static void Main(string[] args)
        {
          var appdomain = AppDomain.CreateDomain("test");
          var test = appdomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Test).FullName) as Test;
          test.Run();
      
          for (int i = 0; i < 10; i++)
          {
            Thread.CurrentThread.Join(500);
            Console.WriteLine("A " + i);
          }
        }
      }
      
      public class Test : MarshalByRefObject
      {
        public void Run()
        {
          for (int i = 0; i < 10; i++)
          {
            Thread.CurrentThread.Join(500);
            Console.WriteLine("B " + i);
          }
        }
      }

    I hope this helps.
    Munir

     

    Thursday, June 3, 2010 2:07 PM