5

Uso il Simple Injector come contenitore IoC. SimpleInjector utilizza this simple technique to handle mixed life style for Per Thread and Per Web RequestUso delle dipendenze su più thread con Parallel.ForEach

container.RegisterPerWebRequest<IWebRepository, Repository>(); 
container.RegisterLifetimeScope<IThreadRepository, Repository>(); 
container.Register<IRepository>(container.GetInstance<Repository>()); 

// Register as hybrid PerWebRequest/PerLifetimeScope. 
container.Register<Repository>(() => 
{ 
    Repository repository; 
    if (HttpContext.Current != null) 
     repository = (Repository)container.GetInstance<IWebRepository>(); 
    else 
     repository = (Repository)container.GetInstance<IThreadRepository>(); 

    return repository; 
}); 

Purtroppo (e, ovviamente!), Altrove nella mia classe UnitOfWork questo mi sta dando un problema quando uso Parallel.ForEach e cerco di mettere in più istanze della classe Repository in parallelo, come solo la prima dei fili rileva un valore in HttpContext.Current

using (TransactionScope scope = new TransactionScope()) 
{ 
    Parallel.ForEach(new List<IRepository>() { _repository1, _repository2 ... }, 
     (repository) => 
     { 
      repository.Commit(); 
     }); 
    scope.Complete(); 
} 

Ora che ho finito di scrivere la domanda posso vedere che sto probabilmente chiedere l'impossibile o qualcosa di stupido ... ma che diavolo. .. Può essere fatto? È possibile rendere disponibile una singola richiesta/registrazione di thread a più thread interni?

risposta

7

Con l'iniezione di dipendenza, si tenta di centralizzare la conoscenza della durata degli oggetti. Questo luogo centralizzato è chiamato Composition Root. Quando inizi a passare le dipendenze da un thread all'altro, quelle parti del codice devono sapere se è sicuro passare tali dipendenze. Ad esempio, queste dipendenze sono thread-safe? Quelle dipendenze possono essere eseguite in quel nuovo contesto (al di fuori di una richiesta HTTP o WCF per esempio)? Questo potrebbe essere banale da analizzare in molte situazioni, ma ti impedisce di cambiare quelle dipendenze con altre implementazioni, poiché ora devi ricordare che c'è un posto nel tuo codice in cui questo sta accadendo ed è necessario sapere quali dipendenze vengono trasmesse. Stai decentrando di nuovo questa conoscenza, rendendo più difficile ragionare sulla correttezza della tua configurazione DI e rendendo più facile configurare erroneamente il contenitore in un modo che faccia fallire il tuo codice direttamente (nel migliore dei casi) o causa difficili condizioni di gara (nel peggiore dei casi).

È quindi più sicuro lasciare che ogni nuovo thread avviato generi un nuovo grafo di oggetti chiedendo al contenitore. Non passare le dipendenze (ovvero le istanze gestite e iniettate dal contenitore) da un thread all'altro.

Nel tuo caso sembra che tu abbia riscontrato problemi poiché i tuoi repository sembrano avere una relazione con il contesto http perché non sembrano essere trasmissibili ad altri thread. In tal caso è abbastanza semplice: non farlo ;-)

Ciò non significa che non è possibile eseguire il multithreading per accelerare le prestazioni in alcun modo. È tuttavia necessario assicurarsi che questa operazione non sposti le dipendenze da un thread a thread o quando lo fanno; assicurati che questo codice (nel tuo caso lo Parallel.ForEach) si trovi all'interno della Composizione Root della tua applicazione, poiché questo è l'unico posto che può/dovrebbe sapere se questa operazione è sicura da eseguire.

Nel tuo caso specifico, tuttavia, trovo piuttosto spaventoso che ti stia impegnando su più thread. Non dovrebbe essere atomico il commit di quell'unità di lavoro? Sei sicuro che questo commit sia ancora atomico quando esegui il repository su diversi thread? Cosa succede quando uno dei commit fallisce ma altri riescono? Come si ripristinano le operazioni già riuscite? Penso che tu possa (e potresti già averlo) risolto questi problemi, ma tale soluzione aggiunge molta complessità extra al sistema. È necessario chiedersi seriamente se il miglioramento delle prestazioni compensa effettivamente con la complessità extra aggiunta al sistema.

È possibile trovare ulteriori informazioni sull'utilizzo dell'integrazione delle dipendenze nelle applicazioni multi-thread here.