UnityContainer ThreadSafe ?

Nov 15, 2013 at 12:58 PM
Edited Nov 15, 2013 at 1:23 PM
i tried fllowing code :


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;

namespace UTest
{
public class Class1
{
    public static void  Main()
    {
        new Class1().Test();
        Console.ReadKey();           
    }

    IUnityContainer containter;
    public Class1()
    {
        containter = new Microsoft.Practices.Unity.UnityContainer();            
    }

    public void Test()
    {
        var ts = new List<System.Threading.Thread>();
        for (int i = 0; i < 1000; i++)
        {
            var t = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadCall));
            ts.Add(t);
            t.Start();
        }
        foreach (var item in ts)
        {
            item.Join();
        }

    }
    public void ThreadCall()
    {
        containter.RegisterInstance<IParent>(new Parent());
        try
        {
            containter.Resolve<Child>();      
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }            
    }
}


public class Child
{
    [InjectionConstructor]
    public Child(IParent p)
    {
        parent = p;
    }

    IParent parent;
}

public interface IParent
{

}

public class Parent:IParent
{

}


}

Test Result

Resolution of the dependency failed, type = "UTest.Child", name = "(none)".
Exception occurred while: while resolving.

Exception is: InvalidOperationException - The current type, UTest.IParent, is an interface and cannot be constructed. Are you missing a type mapping?

At the time of the exception, the container was:

Resolving UTest.Child,(none)
Resolving parameter "p" of constructor UTest.Child(UTest.IParent p)
Resolving UTest.IParent,(none)
Resolution of the dependency failed, type = "UTest.Child", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, UTest.IParent, is an interface and cannot be constructed. Are you missing a type mapping?
Nov 15, 2013 at 5:50 PM
Why are you initializing your container over multiple threads? Stick to good practice, which is:
  1. Initialize the container in one place in one thread (during application startup).
  2. Don't mix initialization and resolving. Only resolve after you're completely done with the initialization.
  3. You can resolve over as many threads as you like.
Nov 16, 2013 at 6:36 AM
Resolve operations and BuildUp are thread safe so you shouldn't see any problems resolving from multiple threads. However, Register methods are not thread-safe (actually according to this post all other methods on the container are not thread-safe).

The approach given by @dot_NET_Junkie is a good approach. If you need to deviate from that approach (and perform registrations while resolving) then you will need to manage locking in order to guarantee thread safety.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Nov 16, 2013 at 8:35 AM
i need a serivce , each thread own each UserContext to be Injection into object.
thank for @dot_NET_Junkie ,@randylevy
i'v tried

lock(this)
{
containter.RegisterInstance<IParent>(new Parent());
}

failed
Nov 16, 2013 at 11:49 PM
@nif_lens, thanks for providing more context. I'm assuming that the IParent is analogous to the UserContext and you want to inject a separate IParent for every thread.

The code you posted is actually not using it's own Parent instance per thread. The Parent is created and then registered as a singleton in the container. But this is a global registration that every other thread will see so I don't think that's what you want.

If the UserContext is capable of being constructed by the container the I would recommend registering it with a PerThreadLifetimeManager and let Unity manage the lifetime for you. This lifetime manager will result in a singleton per thread. It's cleaner and easier:
    public class Class1
    {
        private List<IParent> parents = new List<IParent>();
        private object locker = new object();

        public static void Main()
        {
            new Class1().Test();
        }

        IUnityContainer container;

        public Class1()
        {
            container = new Microsoft.Practices.Unity.UnityContainer();
            container.RegisterType<IParent, Parent>(new PerThreadLifetimeManager());
        }

        public void Test()
        {
            var ts = new List<System.Threading.Thread>();

            for (int i = 0; i < 1000; i++)
            {
                var t = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadCall));
                ts.Add(t);
                t.Start();
            }

            foreach (var item in ts)
            {
                item.Join();
            }

            Console.WriteLine("Did we get any duplicate parent instances? " + DoesListContainDuplicateInstances());
        }

        bool DoesListContainDuplicateInstances()
        {
            return parents.Distinct().Count() != parents.Count;
        }

        void AddParent(IParent parent)
        {
            lock (locker)
            {
                parents.Add(parent);
            }
        }

        public void ThreadCall()
        {
            try
            {
                IParent parent = new Parent();

                var child1 = container.Resolve<Child>();
                var child2 = container.Resolve<Child>();

                System.Diagnostics.Debug.Assert(ReferenceEquals(child1.parent, child2.parent));

                AddParent(child1.parent);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    public class Child
    {
        public Child(IParent p)
        {
            parent = p;
        }

        public IParent parent;
    }

    public interface IParent { }

    public class Parent : IParent { }

If it doesn't make sense for the container to manage the object then you could use a DependencyOverride to pass in the instance you want:
        public void ThreadCall()
        {
            try
            {
                IParent parent = new Parent();

                var child1 = container.Resolve<Child>(new DependencyOverride<IParent>(parent));
                var child2 = container.Resolve<Child>(new DependencyOverride<IParent>(parent));

                System.Diagnostics.Debug.Assert(ReferenceEquals(child1.parent, child2.parent));

                AddParent(child1.parent);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

Or another approach would be to create a Child Container per thread and then register the instance with the child container:
        public void ThreadCall()
        {
            try
            {
                IUnityContainer childContainer = container.CreateChildContainer();
                childContainer.RegisterInstance<IParent>(new Parent());

                var child1 = childContainer.Resolve<Child>();
                var child2 = childContainer.Resolve<Child>();

                System.Diagnostics.Debug.Assert(ReferenceEquals(child1.parent, child2.parent));

                AddParent(child1.parent);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Nov 17, 2013 at 8:39 PM
Refrain from giving services a per-thread lifestyle; the per-thread lifestyle should be considered harmful.
Nov 26, 2013 at 2:33 AM
i chang to create a Child Container per thread ,it work fine!
thank for Randy Levy