2015-05-27 16 views
5

Aggiungo l'iniezione di dipendenza alla mia libreria e per tale motivo utilizzo Unity. E mi chiedo se ho bisogno di fare alcuni passi aggiuntivi per rendere Unity Container thread-safe. Ho trovato un paio di articoli che stanno parlando di contenitori thread-safe (esempio: http://www.fascinatedwithsoftware.com/blog/post/2012/01/04/A-Thread-Safe-Global-Unity-Container.aspx) ma non capisco se ne ho davvero bisogno nel mio progetto. Da un lato non voglio avere alcuni bug sgradevoli a causa delle condizioni di gara dall'altro lato non vedo in quale caso si verificheranno le condizioni di gara. Voglio usare l'unità con il modello Composizione Root e registrare tutti i tipi nel costruttore statico come quello:quali sono le insidie ​​di rendere UnityContainer non thread sicuro?

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>(); 
     } 
    } 

Quindi, in sostanza la mia domanda è: in quali casi ho bisogno ulteriore thread-safe quando lavoro con Unity DI? Dove posso ottenere condizioni di gara o problemi con la sicurezza del filo?

risposta

8

Unity (e tutti i contenitori comuni) sono garantiti (da parte dei progettisti) per essere thread-safe (o almeno, sort of) in caso di risolve parallele senza registrazioni. In altre parole, se si separa la fase di registrazione dalla fase di risoluzione e da un punto solo sulla risoluzione dal contenitore, è possibile chiamare Resolve in parallelo da più thread senza problemi.

In effetti, come best practice, dovresti sempre separare rigorosamente la fase di registrazione dalla fase di risoluzione, perché questo porterà a problemi seri e molto difficili da trovare nelle condizioni di gara.

Questa separazione di quelle fasi è così importante, che alcune librerie DI (come ad esempio Autofac e Simple Injector) Forza questo modello su di voi (dove Semplice Injector è la più rigorosa dei due). La documentazione di Simple Injector contiene un very clear explanation sul perché Simple Injector ti costringe su questo modello e spiega cosa potrebbe accadere nel caso in cui tu potessi cambiare la configurazione. Per citare parte di quella spiegazione qui (ma si dovrebbe assolutamente leggere l'intero spiegazione):

Problemi con thread-sicurezza possono facilmente emergere quando l'utente modifica una registrazione nel corso di una richiesta Web. Se il contenitore consentiva tali modifiche di registrazione durante una richiesta, altre richieste potrebbero direttamente essere modificate da (poiché in generale dovrebbe essere solo un'istanza Contenitore per AppDomain). A seconda di cose come lo stile di vita della registrazione; l'uso delle fabbriche e il modo in cui è strutturato il grafico dell'oggetto , potrebbe essere una reale possibilità che un'altra richiesta di ottenga sia la vecchia che la nuova registrazione. Prendete ad esempio una registrazione temporanea che viene sostituita con una diversa. Se viene eseguito mentre viene risolto un grafico oggetto per un thread diverso mentre il servizio viene iniettato in più punti all'interno del grafico - il grafico conterrà un'istanza diversa di tale astrazione con diverse durate contemporaneamente nello stesso tempo richiesta - e questo è male.

come la vedo io, che the article si collega va più nella differenza tra l'applicazione di Dependency Injection Service Locator anti-pattern e corretto, il che significa che solo l'accesso al contenitore all'interno del vostro Composition Root. Lo scrittore di quell'articolo (Larry Spencer) non è molto chiaro, ma all'interno della sua Composition Root, crea un singolo contenitore Unity e lo usa per tutta la durata dell'intera applicazione.In un certo senso è ancora "globale", ma impedisce l'accesso a tale istanza tramite l'applicazione (perché questo è il pattern Locator di servizio).

Sebbene lo scrittore cerchi di creare un wrapper thread-safe attorno al contenitore Unity, il suo tentativo è ingenuo. Quello che fa è creare un blocco attorno ad ogni metodo Register e Resolve. Ciò non solo consentirà un'enorme congestione nelle applicazioni multi-thread, ma non affronterà i problemi di ciò che accade durante la registrazione e la sostituzione delle istanze dopo che i grafici degli oggetti sono già stati compilati e memorizzati nella cache, come spiega la documentazione di Simple Injector e questo Unity question .

+2

questa risposta è semplicemente fantastica, questo è quello che stavo cercando! Grazie! – vmg

3

In caso di iniezioni di dipendenza, le considerazioni sulla sicurezza del filo raggiungono un livello più profondo rispetto al livello del contenitore. Anche il tipo di dipendenza che si registra nel Container ha un grande significato. Nella maggior parte dei casi, le dipendenze che stai registrando nel contenitore sono singleton. Se il tuo contenitore è statico, è globale per tutti i thread. In altre parole, ogni thread avrà accesso alla stessa istanza singleton. Pertanto, se la dipendenza che si sta registrando mantiene uno stato (stateful), è necessario considerare che altri thread potrebbero cambiare tale stato. Per evitare questo tipo di mal di testa:

1) È possibile limitare la registrazione di dipendenze senza stato.

2) È possibile creare un'istanza di unità [ThreadStatic]. In tal caso, ogni thread avrà la propria istanza di unità e le dipendenze stateful saranno meno un problema.

3) L'opzione migliore è utilizzare PerThreadLifetimeManager di Unity per dipendenze con stato. Che garantirà che ogni thread avrà la propria istanza della dipendenza.

+1

Generalmente non consiglio di avere un contenitore per thread, perché ciò complicherebbe enormemente le cose, ma sono assolutamente d'accordo con la creazione di componenti registrati come single. Questo rende la tua vita molto più semplice. +1 – Steven

Problemi correlati