Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what are pitfalls of making UnityContainer not thread safe?

I am adding dependency injection to my library and I use Unity for that. And I am wondering if I need to take some additional steps to make Unity Container thread-safe. I found couple of articles that are talking about Thread-safe container (example: http://www.fascinatedwithsoftware.com/blog/post/2012/01/04/A-Thread-Safe-Global-Unity-Container.aspx ) but I don't understand if I really need it in my project. From one side I don't want to have some nasty bugs due to race conditions from the other side I don't see in what case race condition would occur. I want to use Unity with Composition Root pattern and register all the types in the static constructor like that:

internal static class ConfiguredUnityContainer
    {
        private static readonly UnityContainer Container = new UnityContainer();

        static ConfiguredUnityContainer()
        {
            Container.RegisterType<IConnectionFactory<SqlConnection>>();
        }

        public static T Resolve<T>()
        {
            return Container.Resolve<T>();
        }
    }

So, basically my question is: in what cases do I need additional thread-safe when I work with Unity DI? Where could I get race conditions or problems with thread-safety?

like image 763
vmg Avatar asked May 27 '15 19:05

vmg


1 Answers

Unity (and all the common containers) are guaranteed (by their designers) to be thread-safe (or at least, sort of) in case of parallel resolves with no registrations. In other words, as long as you separate the registration phase from the resolve phase, and from one point on only resolve from the container, you can call Resolve in parallel from multiple threads without problems.

As a matter of fact, as a best practice, you should always strictly separate the registration phase from the resolve phase, because this will lead to serious trouble and very hard to find race conditions.

This separation of those phases is so important, that some DI libraries (such as Autofac and Simple Injector) force this pattern upon you (where Simple Injector is the strictest of the two). The Simple Injector documentation contains a very clear explanation on why Simple Injector forces you upon this model and explains what could happen in case you would be able to change the configuration. To quote part of that explanation here (but you should definitely read the whole explanation):

Problems with thread-safety can easily emerge when the user changes a registration during a web request. If the container allowed such registration changes during a request, other requests could directly be impacted by those changes (since in general there should only be one Container instance per AppDomain). Depending on things such as the lifestyle of the registration; the use of factories and how the object graph is structured, it could be a real possibility that another request gets both the old and the new registration. Take for instance a transient registration that is replaced with a different one. If this is done while an object graph for a different thread is being resolved while the service is injected into multiple points within the graph - the graph would contain different instance of that abstraction with different lifetimes at the same time in the same request - and this is bad.

As I see it, that the article you are linking goes more into the difference between the Service Locator anti-pattern and applying Dependency Injection correctly, which means only accessing the container inside your Composition Root. The writer of that article (Larry Spencer) isn't very clear, but inside his Composition Root, he creates one single Unity container and uses it for the duration of the whole application. In a sense it is still 'global', but he prevents that instance from being accessed through the application (because that is the Service Locator pattern).

Although the writer tries to create a thread-safe wrapper around the Unity container, his attempt is naive. What he does is creating a lock around every Register and Resolve method. Not only will this give enormous congestion in multi-threaded applications, it doesn't address the problems of what happens when registering and replacing instances after object graphs have already been build and cached, as both the Simple Injector documentation explains and this Unity question shows.

like image 104
Steven Avatar answered Sep 18 '22 10:09

Steven