5

Używam Simple Injector jako mojego kontenera IoC. SimpleInjector wykorzystuje this simple technique to handle mixed life style for Per Thread and Per Web RequestKorzystanie z zależności na wielu wątkach z 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; 
}); 

Niestety (i oczywiście!), Gdzie indziej w mojej klasie UnitOfWork to daje mi problem podczas korzystania Parallel.ForEach i spróbuj zadzwonić do wielu wystąpień klasy Repository równolegle jak tylko pierwszy z wątków wyszukuje wartość w HttpContext.Current

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

teraz, gdy skończę pisać na pytanie widzę ja prawdopodobnie prośbą o niemożliwe lub coś głupiego ... ale co do cholery. ... czy można to zrobić? Czy pojedyncza rejestracja zgłoszeń/wątków może zostać udostępniona wielu wątkom wewnętrznym?

Odpowiedz

7

Z zastrzykiem zależności próbujesz scentralizować wiedzę o czasie życia obiektów. To scentralizowane miejsce nazywa się Composition Root. Kiedy zaczynasz przekazywać zależności z jednego wątku na drugi, te części kodu muszą wiedzieć, czy przekazywanie tych zależności jest bezpieczne. Na przykład, czy te zależności są bezpieczne dla wątków? Czy te zależności mogą działać w tym nowym kontekście (na przykład poza żądaniem HTTP lub WCF)? To może być trywialne do analizy w wielu sytuacjach, ale uniemożliwia zmianę tych zależności w innych implementacjach, ponieważ teraz musisz pamiętać, że w kodzie jest miejsce, w którym to się dzieje i musisz wiedzieć, które zależności są przekazywane. Ponownie zdecentralizujesz tę wiedzę, utrudniając rozumowanie o poprawności konfiguracji DI i ułatwiając błędne skonfigurowanie kontenera w sposób, który powoduje, że Twój kod zawodzi bezpośrednio (w najlepszym przypadku) lub powoduje trudne do debugowania warunki wyścigu (w najgorszym).

Dlatego najbezpieczniej jest, aby każdy nowo rozpoczęty wątek tworzył nowy wykres obiektów, prosząc o to pojemnik. Nie przekazuj zależności (to znaczy: instancji, które są zarządzane i wstrzykiwane przez kontener) z jednego wątku na drugi.

W twoim przypadku wydaje się, że masz problemy, ponieważ twoje repozytoria wydają się mieć związek z kontekstem http, ponieważ nie wydają się być przenoszone na inne wątki. W takim przypadku jest to dość łatwe: nie rób tego ;-)

Nie oznacza to, że nie można wykonywać wielowątkowości, aby w jakikolwiek sposób przyspieszyć działanie. Musisz jednak upewnić się, że ta operacja nie przenosi zależności z jednego wątku do wątku, lub kiedy to robią; upewnij się, że ten kod (w twoim przypadku Parallel.ForEach) znajduje się w głównym katalogu kompozycji, ponieważ jest to jedyne miejsce, które może/powinien wiedzieć, czy operacja jest bezpieczna do wykonania.

W twoim konkretnym przypadku jednak uważam za dość przerażające, że popełniasz wiele wątków. Czy zatwierdzenie tej jednostki pracy nie powinno być atomowe? Czy jesteś pewien, że to zatwierdzenie jest nadal atomiczne, gdy wykonujesz commit repozytorium na różnych wątkach? Co się stanie, gdy jedna z prób zakończy się niepowodzeniem, ale inne się powiedzie? W jaki sposób można wycofać już zakończone operacje? Sądzę, że możesz (i być może już) rozwiązałeś te problemy, ale takie rozwiązanie zwiększa dodatkowo złożoność systemu. Musisz poważnie zadać sobie pytanie, czy poprawa wydajności faktycznie rekompensuje dodatkową złożoność dodaną do systemu.

Możesz znaleźć więcej informacji na temat pracy z wtryskiem zależności w aplikacjach wielowątkowych here.

Powiązane problemy