O strategie mai eficienta cand lucram cu ConcurrentDictionary
O strategie mai eficienta cand lucram cu ConcurrentDictionary
ConcurrentDictionary are o trasatura specifica: in unele cazuri poate sa nu se manifeste asa cum ti-ai dorit. Iata un exemplu. Sa presupunem ca trebuie sa facem niste caching astfel incat rezultatele unui calcul sa fie luate din cache, daca sunt acolo, si adaugate cache-ului intr-o maniera transparenta daca cineva a facut o greseala.
18 Feb 2016
2142
Other articles
Object-relational Mapping folosind JPA, Hibernate si Spring Data JPA. Persistence cu JPA
Cum sa interogam Kafka Streaming Data?
Procrastinarea. Care sunt avantajele ei?
Object-relational Mapping folosind JPA, Hibernate si Spring Data JPA
Procrastinarea
Cerinte. De ce avem nevoie de ele?
Dezvolta-ti abilitatile cu training-urile noastre
Programarea reactiva Java. Implementari
Testarea software. Intrebari tipice si raspunsuri. Continuare
Testarea software. Intrebari tipice si raspunsuri
ConcurrentDictionary are o trasatura specifica: in unele cazuri poate sa nu se manifeste asa cum ti-ai dorit. Iata un exemplu. Sa presupunem ca trebuie sa facem niste caching astfel incat rezultatele unui calcul sa fie luate din cache, daca sunt acolo, si adaugate cache-ului intr-o maniera transparenta daca cineva a facut o greseala.
O simpla implementare a unui provider cu un cache aside pattern ar arata asa:

In termeni de multi-threading, aceasta implementare este corecta. Chiar daca metoda RunOperationOrGetFromCache pentru acelasi operationId a fost invocata din doua threaduri, fiecare va avea acelasi rezultat. Problema este ca, desi rezultatul este acelasi, avem doua operatii care merg in acelasi timp. Rezultatul primei operatii va fi plasat in cache si rezultatul celei de-a doua operatii va fi eliminat!
Motivul pentru care se intampla acest lucru consta in implementarea metodei AddOrGet a clasei ConcurrentDictionary. De fapt, utilizarea lui AddOrGet este echivalenta cu utilizarea consistenta a metodelor precum TryGetValue TryAdd in propriul nostru cod (metoda AddOrGet este un pic mai complicata fata de o simpla invocare a acestor doua metode):

Acum, ar trebui sa fie clar ca atunci cand doua threaduri vor intra in conflict si vor incerca simultan sa acceseze aceeasi operatie, o operatie pe termen lung va dura de doua ori mai mult. Insa nu este chiar atat de rau. Din moment ce utilizarea metodei AddOrGet va plasa doar primul rezultat in colectie, putem sa folosim urmatorea solutie:

In loc sa stocam doar rezultatul unei operatii lungi, cacheul va stoca si “lazy shell” – Lazy. In acest caz, cand exista referinte simultane la cache din partea unor threaduri multiple doar un constructor object Lazy<T> va fi invocat de cateva ori si operatia in sine va fi rulata doar o data – cand se acceseaza proprietatea Value!
Sergey Teplyakov
Expert in .Net, С++ and Application Architecture
O simpla implementare a unui provider cu un cache aside pattern ar arata asa:

In termeni de multi-threading, aceasta implementare este corecta. Chiar daca metoda RunOperationOrGetFromCache pentru acelasi operationId a fost invocata din doua threaduri, fiecare va avea acelasi rezultat. Problema este ca, desi rezultatul este acelasi, avem doua operatii care merg in acelasi timp. Rezultatul primei operatii va fi plasat in cache si rezultatul celei de-a doua operatii va fi eliminat!
Motivul pentru care se intampla acest lucru consta in implementarea metodei AddOrGet a clasei ConcurrentDictionary. De fapt, utilizarea lui AddOrGet este echivalenta cu utilizarea consistenta a metodelor precum TryGetValue TryAdd in propriul nostru cod (metoda AddOrGet este un pic mai complicata fata de o simpla invocare a acestor doua metode):

Acum, ar trebui sa fie clar ca atunci cand doua threaduri vor intra in conflict si vor incerca simultan sa acceseze aceeasi operatie, o operatie pe termen lung va dura de doua ori mai mult. Insa nu este chiar atat de rau. Din moment ce utilizarea metodei AddOrGet va plasa doar primul rezultat in colectie, putem sa folosim urmatorea solutie:

In loc sa stocam doar rezultatul unei operatii lungi, cacheul va stoca si “lazy shell” – Lazy. In acest caz, cand exista referinte simultane la cache din partea unor threaduri multiple doar un constructor object Lazy<T> va fi invocat de cateva ori si operatia in sine va fi rulata doar o data – cand se acceseaza proprietatea Value!
Sergey Teplyakov
Expert in .Net, С++ and Application Architecture