Többszálas kód tesztelése Java-ban

1. Bemutatkozás

Ebben az oktatóanyagban bemutatjuk az egyidejű program tesztelésének alapjait. Elsősorban a szálalapú párhuzamosságra és az általa a tesztelés során felmerülő problémákra fogunk összpontosítani.

Megértjük azt is, hogy miként oldhatjuk meg ezeket a problémákat, és hatékonyan tesztelhetjük a többszálas kódot a Java-ban.

2. Egyidejű programozás

Az egyidejű programozás arra a programozásra utal, ahol mi bontson egy nagy számítási darabot kisebb, viszonylag független számításokra.

Ennek a gyakorlatnak az a célja, hogy ezeket a kisebb számításokat egyidejűleg, esetleg párhuzamosan futtassa. Bár ennek elérésére többféle módszer létezik, a cél mindig a program gyorsabb futtatása.

2.1. Szálak és egyidejű programozás

Mivel a processzorok több magot csomagolnak, mint valaha, a párhuzamos programozás az élen jár, hogy hatékonyan kihasználhassák azokat. A tény azonban továbbra is az az egyidejű programokat sokkal nehezebb megtervezni, írni, tesztelni és fenntartani. Tehát, ha végül is hatékony és automatizált teszteseteket tudunk írni egyidejű programok számára, akkor ezeknek a problémáknak a nagy részét meg tudjuk oldani.

Szóval, mitől olyan nehéz megírni az egyidejű kód tesztjeit? Ennek megértéséhez meg kell értenünk, hogyan érjük el a programjaink párhuzamosságát. Az egyik legnépszerűbb egyidejű programozási technika magában foglalja a szálak használatát.

Most a szálak lehetnek natívak, ebben az esetben az alapul szolgáló operációs rendszerek ütemezik őket. Használhatunk úgynevezett zöld szálakat is, amelyeket közvetlenül egy futás ütemez.

2.2. Nehézség az egyidejű programok tesztelésében

Függetlenül attól, hogy milyen típusú szálakat használunk, ami a szálkommunikációt megnehezíti. Ha valóban sikerül olyan programot írni, amely szálakat tartalmaz, de nincs szálkommunikáció, nincs annál jobb! Valójában a szálaknak általában kommunikálniuk kell. Ennek kétféle módja van - megosztott memória és üzenet továbbítása.

A nagy része a párhuzamos programozással kapcsolatos probléma abból adódik, hogy natív szálakat használunk megosztott memóriával. Az ilyen programok tesztelése ugyanezen okokból nehéz. A megosztott memóriához hozzáférő több szál általában kölcsönös kizárást igényel. Ezt általában valamilyen zárakkal ellátott védőmechanizmus révén érjük el.

De ez még mindig számos problémához vezethet, mint például a versenyfeltételek, az élő zárak, a holtpontok és a szálas éhezés, hogy csak néhányat említsünk. Sőt, ezek a problémák időszakosak, mivel a szál ütemezése a natív szálak esetében teljesen nem determinisztikus.

Ezért hatékony tesztek írása párhuzamos programok számára, amelyek képesek ezeket a problémákat determinisztikus módon észlelni, valóban kihívást jelent!

2.3. A szálak összefonódásának anatómiája

Tudjuk, hogy a natív szálakat kiszámíthatatlanul ütemezhetik az operációs rendszerek. Abban az esetben, ha ezek A szálak hozzáférnek és módosítják a megosztott adatokat, ez érdekes szálak átszövését eredményezi. Míg ezek a közbeszólások némelyike ​​teljesen elfogadható lehet, mások a végleges adatokat nemkívánatos állapotban hagyhatják.

Vegyünk egy példát. Tegyük fel, hogy van egy globális számlálónk, amelyet minden szál növekszik. A feldolgozás végére azt szeretnénk, hogy ennek a számlálónak az állapota pontosan megegyezzen a végrehajtott szálak számával:

magán int számláló; public void increment () {számláló ++; }

Most, hogy a primitív egész szám növelése a Java-ban nem atomi művelet. Ez az érték kiolvasásából, növeléséből és végül mentéséből áll. Míg több szál ugyanazt a műveletet végzi, ez számos lehetséges közbeszólást eredményezhet:

Noha ez a különös összeszövés teljesen elfogadható eredményeket hoz, mi a helyzet ezzel:

Nem erre számítottunk. Most képzeljen el több száz szálat, amelyek ennél sokkal összetettebb kódot futtatnak. Ez elképzelhetetlen módszereket eredményez a szálak összefonódására.

A kódírásra többféleképpen lehet elkerülni ezt a problémát, de ez nem a jelen oktatóanyag témája. A zár használatával történő szinkronizálás az egyik általános, de problémái vannak a verseny körülményeivel kapcsolatban.

3. Többszálú kód tesztelése

Most, hogy megértettük a többszálas kód tesztelésének alapvető kihívásait, meglátjuk, hogyan lehet ezeket legyőzni. Kiépítünk egy egyszerű használati esetet, és megpróbálunk a lehető legtöbb problémát szimulálni az egyidejűséggel kapcsolatban.

Kezdjük azzal, hogy meghatározunk egy egyszerű osztályt, amely számba veszi az esetleges eseteket:

nyilvános osztály MyCounter {private int count; public void increment () {int temp = count; szám = temp + 1; } // Getter for count}

Ez egy látszólag ártalmatlan kódrészlet, de nem nehéz megérteni, hogy nem szálbiztos. Ha véletlenül egyidejű programot írunk ezzel az osztállyal, akkor az biztosan hibás lesz. A tesztelés célja az ilyen hibák azonosítása.

3.1. Nem egyidejű alkatrészek tesztelése

Mint egy ökölszabály, mindig tanácsos tesztelni a kódot úgy, hogy elkülönítse az esetleges párhuzamos viselkedéstől. Ennek ésszerű meggyőződése, hogy a kódban nincs más hiba, amely nem kapcsolódik az egyidejűséghez. Lássuk, hogyan tehetjük meg ezt:

@Test public void testCounter () {MyCounter számláló = new MyCounter (); for (int i = 0; i <500; i ++) {számlálónövekedés (); } assertEquals (500, counter.getCount ()); }

Bár itt nincs sok minden, ez a teszt bizalmat ad nekünk arról, hogy legalább egyidejűség hiányában működik.

3.2. Első kísérlet az egyidejű tesztelésre

Folytassuk, hogy újra teszteljük ugyanazt a kódot, ezúttal egyidejű beállítással. Megpróbáljuk elérni az osztály ugyanazon példányát több szálon, és megnézzük, hogyan viselkedik:

@Test public void testCounterWithConcurrency () dobja az InterruptedException {int numberOfThreads = 10; ExecutorService service = Executors.newFixedThreadPool (10); CountDownLatch retesz = new CountDownLatch (numberOfThreads); MyCounter számláló = new MyCounter (); for (int i = 0; i {számlálónövekedés (); retesz.szám count ();}); } retesz.várakozik (); assertEquals (numberOfThreads, counter.getCount ()); }

Ez a teszt ésszerű, mivel több szálon osztott adatokat próbálunk működtetni. Mivel alacsonyan tartjuk a szálak számát, például 10-et, észrevehetjük, hogy szinte folyamatosan halad. Érdekes módon, ha elkezdjük növelni a szálak számát, mondjuk 100-ra, látni fogjuk, hogy a teszt legtöbbször kudarcot vall.

3.3. Jobb kísérlet az egyidejű tesztelésre

Míg az előző tesztből kiderült, hogy a kódunk nem biztonságos a menetben, probléma van ezzel a bimbóval. Ez a teszt nem determinisztikus, mert az alapul szolgáló szálak nem determinisztikusan szelvényeznek. Programunkban valóban nem támaszkodhatunk erre a tesztre.

Amire szükségünk van a szálak összefonódásának ellenőrzésének módja, hogy felfedhessük a párhuzamossági kérdéseket determinisztikus módon, sokkal kevesebb szálal. Kezdjük egy kicsit módosítani a tesztelt kódot:

public synchronized void increment () dobja az InterruptedException {int temp = count; várakozás (100); gróf = hőmérséklet + 1; }

Itt elkészítettük a módszert szinkronizált és bevezetett egy várakozást a módszer két lépése között. A szinkronizált kulcsszó biztosítja, hogy csak egy szál módosíthatja a számol változó egyszerre, és a várakozás késleltetést vezet be az egyes szálak végrehajtása között.

Felhívjuk figyelmét, hogy nem feltétlenül kell módosítanunk a tesztelni kívánt kódot. Mivel azonban sokféleképpen nem befolyásolhatjuk a szálak ütemezését, ehhez folyamodunk.

Egy későbbi szakaszban megtudjuk, hogyan tehetjük ezt meg a kód megváltoztatása nélkül.

Most teszteljük hasonlóan ezt a kódot, mint korábban:

@Test public void testSummationWithConcurrency () dobja az InterruptedException {int numberOfThreads = 2; ExecutorService service = Executors.newFixedThreadPool (10); CountDownLatch retesz = new CountDownLatch (numberOfThreads); MyCounter számláló = new MyCounter (); for (int i = 0; i {próbáld meg {counter.increment ();} catch (InterruptedException e) {// Handle kivétel} latch.countDown ();}); } retesz.várakozik (); assertEquals (numberOfThreads, counter.getCount ()); }

Itt ezt csak két szálon futtatjuk, és valószínű, hogy képesek leszünk megszerezni a hiányzott hibát. Amit itt tettünk, az az volt, hogy megpróbáljunk elérni egy adott szálat, amelyről tudjuk, hogy hatással lehet ránk. Bár jó a demonstrációhoz, lehet, hogy ezt nem találjuk hasznosnak gyakorlati célokra.

4. Rendelkezésre álló tesztelő eszközök

Amint a szálak száma növekszik, az egymásba illeszkedésük lehetséges száma exponenciálisan növekszik. Ez az csak nem lehet kitalálni az összes ilyen közbeszólást és tesztelni őket. Eszközökre kell támaszkodnunk, hogy azonos vagy hasonló erőfeszítéseket tegyünk számunkra. Szerencsére van pár közülük, hogy megkönnyítsük az életünket.

Kétféle eszköz áll a rendelkezésünkre az egyidejű kód tesztelésére. Az első lehetővé teszi számunkra, hogy meglehetősen nagy feszültséget okozzunk a sok szálat tartalmazó párhuzamos kóddal. A stressz növeli a ritka összefonódás valószínűségét, és ezáltal növeli az esélyünket a hibák megtalálására.

A második lehetővé teszi számunkra, hogy szimuláljuk az egyes szálak átszövését, ezáltal nagyobb biztonsággal segítünk megtalálni a hibákat.

4.1. tempus-fugit

A tempus-fugit Java könyvtár segít könnyedén írni és tesztelni a párhuzamos kódot. Itt csak ennek a könyvtárnak a tesztrészére fogunk összpontosítani. Korábban láttuk, hogy a stressz előállítása több szálú kódra növeli az esélyt az egyidejűséggel kapcsolatos hibák megtalálásának esélyére.

Míg mi is írhatunk segédprogramokat a stressz előállítására, a tempus-fugit kényelmes módokat kínál ennek elérésére.

Vizsgáljuk meg újra ugyanazt a kódot, amelyhez korábban stresszt próbáltunk létrehozni, és értsük meg, hogyan érhetjük el ugyanezt a tempus-fugit használatával:

public class MyCounterTests {@Rule public ConcurrentRule concurrently = new ConcurrentRule (); @Rule public RepeatingRule rule = new RepeatingRule (); privát statikus MyCounter számláló = new MyCounter (); @Test @Concurrent (count = 10) @Repeating (repetition = 10) public void runMultipleTimes () {counter.increment (); } @AfterClass public static void annotatedTestRunsMultipleTimes () thrumps InterruptedException {assertEquals (counter.getCount (), 100); }}

Itt a következők közül kettőt használunk Szabálys elérhető a tempus-fugit-ról. Ezek a szabályok elfogják a teszteket, és segítenek a kívánt viselkedés, például az ismétlés és a párhuzamosság alkalmazásában. Tehát gyakorlatilag tíz különböző szálból tízszer ismételjük meg a tesztelt műveletet.

Az ismétlés és az egyidejűség növelésével nő az esélyünk a párhuzamossággal kapcsolatos hibák észlelésére.

4.2. Cérnaszövő

A Thread Weaver lényegében az Java keretrendszer a többszálas kód teszteléséhez. Korábban láthattuk, hogy a szálak összeszövése meglehetősen kiszámíthatatlan, ezért rendszeres tesztekkel soha nem találhatunk bizonyos hibákat. Amire valójában szükségünk van, az a módszer, hogy ellenőrizzük a váltakozásokat és teszteljük az összes lehetséges összeszövést. Ez korábbi kísérletünk során meglehetősen összetett feladatnak bizonyult.

Nézzük meg, hogy a Thread Weaver hogyan segíthet nekünk itt. A Thread Weaver lehetővé teszi számunkra, hogy két különálló szál végrehajtását nagyszámú módon egymásba illesszük, anélkül, hogy aggódnunk kellene, hogyan. Ez lehetőséget nyújt arra is, hogy finom szemcsésséggel ellenőrizhessük, hogy miként szeretnénk a szálakat összefonódni.

Nézzük meg, hogyan javíthatnánk az előző, naiv kísérletünkön:

nyilvános osztály MyCounterTests {privát MyCounter számláló; @ThreadedBefare public void előtt () {counter = new MyCounter (); } @ThreadedMain public void mainThread () {counter.increment (); } @ThreadedSecondary public void secondThread () {counter.increment (); } @ThreadedAfter public void után () {assertEquals (2, counter.getCount ()); } @Test public void testCounter () {new AnnotatedTestRunner (). RunTests (this.getClass (), MyCounter.class); }}

Itt két szálat definiáltunk, amelyek megpróbálják növelni a számlálónkat. A Thread Weaver megpróbálja futtatni ezt a tesztet ezekkel a szálakkal az összes lehetséges átlapolási forgatókönyvben. Esetleg az egyik közbeiktatásban megkapjuk a hibát, ami teljesen nyilvánvaló a kódunkban.

4.3. TöbbszálúTC

A MultithreadedTC az egy újabb keretrendszer az egyidejű alkalmazások teszteléséhez. Van egy metronóm, amelyet a több szálon végzett tevékenységek sorrendjének finom szabályozására használnak. Támogatja azokat a teszteseteket, amelyek a szálak speciális átszövését gyakorolják. Ezért ideális esetben képesnek kell lennünk arra, hogy minden jelentős összefonódást determinisztikusan teszteljünk egy külön szálban.

Most ennek a funkciókban gazdag könyvtárnak a teljes bemutatása meghaladja az oktatóanyag kereteit. De biztosan láthatjuk, hogyan állíthatunk be gyorsan olyan teszteket, amelyek lehetővé teszik számunkra a szálak végrehajtását.

Lássuk, hogyan tesztelhetjük kódunkat determinisztikusabban a MultithreadedTC segítségével:

public class MyTests kiterjeszti a MultithreadedTestCase {private MyCounter számlálót; @Orride public void Initialize () {számláló = új Sajátszámláló (); } public void thread1 () dobja az InterruptedException {counter.increment (); } public void thread2 () dobja az InterruptedException {counter.increment (); } @Orride public void finish () {assertEquals (2, counter.getCount ()); } @Test public void testCounter () Throwable {TestFramework.runManyTimes (new MyTests (), 1000) dob; }}

Itt két szálat állítunk fel a megosztott számláló működtetésére és növelésére. A MultithreadedTC-t úgy konfiguráltuk, hogy ezekkel a szálakkal végezze el ezt a tesztet akár ezer különböző közbeszólásig, amíg nem észleli a sikertelenet.

4.4. Jáva jcstress

Az OpenJDK karbantartja a Code Tool Project fejlesztői eszközöket az OpenJDK projektekkel való munkához. A projekt során számos hasznos eszköz létezik, köztük a Java Concurrency Stress Tests (jcstress). Ezt kísérleti hevederként és tesztcsomagként fejlesztik, hogy megvizsgálják a Java párhuzamossági támogatásának helyességét.

Habár ez egy kísérleti eszköz, mégis felhasználhatjuk ezt az egyidejű kód elemzésére és tesztek írására a vele kapcsolatos hibák finanszírozására. Lássuk, hogyan tesztelhetjük a kódot, amelyet eddig ebben az oktatóanyagban használtunk. A koncepció felhasználási szempontból meglehetősen hasonló:

@JCStressTest @Outcome (id = "1", várható = ACCEPTABLE_INTERESTING, desc = "Egy frissítés elveszett.") @Outcome (id = "2", várható = ACCEPTABLE, desc = "Mindkét frissítés") @State public class MyCounterTests {privát MyCounter számláló; @Actor public void színész1 () {counter.increment (); } @Actor public void színész2 () {számláló.növekedés (); } @Arbiter public void arbiter (I_Result r) {r.r1 = counter.getCount (); }}

Itt egy osztályzattal megjelöltük az osztályt Állapot, ami azt jelzi, hogy több szál által mutált adatokat tartalmaz. Emellett kommentárokat használunk Színész, amely megjelöli azokat a módszereket, amelyek megtartják a különböző szálak által végrehajtott műveleteket.

Végül van egy módszerünk, amelyet annotációval jelölünk Döntőbíró, amely lényegében csak egyszer látogatja meg az államot Színészs meglátogatták. Használtunk annotációt is Eredmény hogy meghatározzuk elvárásainkat.

Összességében a beállítás meglehetősen egyszerű és intuitív módon követhető. Ezt futtathatjuk a keretrendszer által adott tesztköteggel, amely az összes osztályt feljegyzi JCStressTest és több ismétlésben végrehajtja őket, hogy minden lehetséges közbeszólást megszerezzen.

5. A párhuzamossági problémák észlelésének egyéb módjai

Teszteket írni egyidejű kódra nehéz, de lehetséges. Láttuk a kihívásokat és néhány népszerű módszert azok leküzdésére. Azonban, előfordulhat, hogy csak tesztekkel nem tudjuk azonosítani az összes lehetséges párhuzamossági kérdést - különösen akkor, ha a további tesztek megírásával járó többletköltségek meghaladják az előnyeiket.

Ennélfogva ésszerű számú automatizált teszttel együtt más technikákat is alkalmazhatunk a párhuzamossági problémák azonosítására. Ez növeli annak esélyét, hogy megtaláljuk a párhuzamossági problémákat anélkül, hogy túlságosan elmélyülnénk az automatizált tesztek összetettségében. Ezek közül néhányat ebben a szakaszban tárgyalunk.

5.1. Statikus elemzés

Statikus elemzés egy program elemzésére utal, anélkül, hogy azt ténylegesen végrehajtanák. Most mire jó egy ilyen elemzés? Erre eljutunk, de először is értsük meg, hogy áll ellentétben a dinamikus elemzéssel. Az eddig írt egységteszteket az általuk tesztelt program tényleges végrehajtásával kell futtatni. Ez az oka annak, hogy részei annak, amit nagyrészt dinamikus elemzésnek nevezünk.

Felhívjuk figyelmét, hogy a statikus elemzés semmilyen módon nem pótolja a dinamikus elemzést. Ez azonban felbecsülhetetlen eszközt nyújt a kódszerkezet megvizsgálására és a lehetséges hibák azonosítására jóval azelőtt, hogy még a kódot végrehajtanánk. A a statikus elemzés sok olyan sablont használ, amelyek tapasztalatok alapján készülnek és megértés.

Bár meglehetősen egyszerű átnézni a kódot, és összehasonlítani az általunk kurált legjobb gyakorlatokkal és szabályokkal, el kell ismernünk, hogy ez nem hihető a nagyobb programok esetében. Számos eszköz áll azonban rendelkezésre az elemzés elvégzésére számunkra. Meglehetősen kiforrottak, és a legnépszerűbb programnyelvek számára hatalmas szabályokkal rendelkeznek.

A Java egyik elterjedt statikus elemző eszköze a FindBugs. A FindBugs keresi a „hibamintákat”. A hibaminta egy olyan kódidióma, amely gyakran hiba. Ez több okból is felmerülhet, mint a nehéz nyelvi jellemzők, a félreértett módszerek és a félreértett invariánsok.

A FindBugs megvizsgálja a Java bájtkódját a hibaminták előfordulása szempontjából a bájtkód tényleges végrehajtása nélkül. Ez nagyon kényelmes használni és gyorsan futtatható. A FindBugs számos kategóriába tartozó hibákat jelent, mint például a feltételek, a tervezés és a duplikált kód.

Ez magában foglalja az egyidejűséggel kapcsolatos hibákat is. Meg kell azonban jegyezni, hogy a FindBugs hamis pozitív eredményeket jelenthet. Ezek a gyakorlatban kevesebbek, de összefüggésben kell lenniük a kézi elemzéssel.

5.2. Modellellenőrzés

A modellellenőrzés módszer annak ellenőrzésére, hogy a rendszer véges állapotú modellje megfelel-e az adott specifikációnak. Ez a meghatározás túl akadémikusnak tűnhet, de viselje el egy darabig!

Tipikusan egy számítási problémát képviselhetünk véges állapotú gépként. Noha ez önmagában is óriási terület, egy olyan modellt ad számunkra, amelynek véges halmaza és a köztük lévő átmenet szabályai világosan meghatározott kezdő és végállapotúak.

Most a A specifikáció meghatározza, hogy a modellnek hogyan kell viselkednie ahhoz, hogy helyesnek tekintsék. Lényegében ez a specifikáció teljesíti a modell által képviselt rendszer összes követelményét. A specifikációk rögzítésének egyik módja az Amir Pnueli által kifejlesztett időbeli logikai képlet használata.

Noha logikailag lehetséges a modellellenőrzés manuális elvégzése, meglehetősen nem praktikus. Szerencsére számos eszköz áll rendelkezésre, amelyek segítségünkre vannak itt.A Java számára elérhető egyik ilyen eszköz a Java PathFinder (JPF). A JPF-et a NASA több éves tapasztalatával és kutatásával fejlesztették ki.

Kimondottan, A JPF a Java bytecode modellellenőrzője. Minden lehetséges módon futtat egy programot, ellenőrizve ezáltal a tulajdonjogi megsértéseket, például a holtpontot és a kezeletlen kivételeket az összes lehetséges végrehajtási útvonal mentén. Ezért nagyon hasznosnak bizonyulhat az egyidejűséggel kapcsolatos hibák felderítésében bármely programban.

6. Utángondolások

Mostanra ez nem lehet meglepetés számunkra a legjobb elkerülni a többszálas kóddal kapcsolatos bonyolultságokat amennyire csak lehetséges. Elsődleges célunk az egyszerűbb kivitelű, könnyebben tesztelhető és fenntartható programok kidolgozása. Egyet kell értenünk abban, hogy a modern alkalmazásokhoz gyakran egyidejű programozásra van szükség.

Azonban, több bevált gyakorlatot és alapelvet alkalmazhatunk, miközben párhuzamos programokat dolgozunk ki ez megkönnyítheti életünket. Ebben a részben áttekintjük ezeket a bevált gyakorlatokat, de nem szabad megfeledkeznünk arról, hogy ez a lista még korántsem teljes!

6.1. Csökkentse a komplexitást

A komplexitás olyan tényező, amely megnehezítheti a program tesztelését párhuzamos elemek nélkül is. Ez csak a párhuzamosság mellett keveredik. Nem nehéz megérteni, miért az egyszerűbb és kisebb programokat könnyebb megindokolni, és ezáltal hatékonyan tesztelni. Számos legjobb minta segíthet nekünk itt, például az SRP (Single Responsibility Pattern) és a KISS (Keep It Stupid Simple), hogy csak néhányat említsünk.

Noha ezek nem foglalkoznak közvetlenül az egyidejű kód tesztjeinek írásával, megkönnyítik a munka megkísérlését.

6.2. Tekintsük az atomműveleteket

Atomi műveletek olyan műveletek, amelyek egymástól teljesen függetlenül futnak. Ennélfogva a szövevény előrejelzésének és tesztelésének nehézségei egyszerűen elkerülhetők. Az összehasonlítás és a csere egy ilyen széles körben alkalmazott atomi utasítás. Egyszerűen fogalmazva, összehasonlítja a memóriahely tartalmát egy adott értékkel, és csak akkor módosítja a memóriahely tartalmát, ha azonosak.

A legtöbb modern mikroprocesszor felajánlja ennek az utasításnak valamilyen változatát. A Java számos atomi osztályt kínál, mint például AtomicInteger és AtomicBoolean, felajánlva az összehasonlítás és a csere utasítások előnyeit.

6.3. A megváltoztathatatlanság befogadása

A többszálas programozásban a módosítható megosztott adatok mindig teret engednek a hibáknak. Állandóság arra a feltételre utal, amikor az adatszerkezetet nem lehet módosítani a példányosítás után. Ez egy meccs, amelyet a mennyországban készítettek egyidejű programok számára. Ha egy objektum állapota a létrehozása után nem módosítható, a versengő szálaknak nem kell kölcsönös kizárást alkalmazniuk rajtuk. Ez nagyban leegyszerűsíti az egyidejű programok írását és tesztelését.

Kérjük, vegye figyelembe, hogy nem mindig van szabadságunk választani a változhatatlanságot, de ezt választanunk kell, amikor ez lehetséges.

6.4. Kerülje a megosztott memóriát

A többszálas programozással kapcsolatos kérdések többsége annak tulajdonítható, hogy megosztott memóriával rendelkezünk a versengő szálak között. Mi lenne, ha csak megszabadulhatnánk tőlük! Nos, még mindig szükségünk van valamilyen mechanizmusra a szálak kommunikációjához.

Vannak alternatív tervezési minták egyidejű alkalmazásokhoz, amelyek ezt a lehetőséget kínálják számunkra. Az egyik népszerű a színészmodell, amely a színészt a párhuzamosság alapegységeként írja elő. Ebben a modellben a színészek üzenetek küldésével lépnek kapcsolatba egymással.

Az Akka a Scalában írt keretrendszer, amely az Actor Model-t használja fel arra, hogy jobb egyidejűségi primitíveket kínáljon.

7. Következtetés

Ebben az oktatóanyagban bemutattuk a párhuzamos programozással kapcsolatos alapokat. Különösen részletesen tárgyaltuk a Java többszálú egyidejűségét. Az ilyen kód tesztelése során átéltük a kihívásokat, amelyeket számunkra jelent, különösen megosztott adatokkal. Továbbá átnéztünk néhány eszközt és technikát az egyidejű kód teszteléséhez.

Megbeszéltük a párhuzamossági problémák elkerülésének egyéb módjait is, beleértve az automatizált teszteken kívüli eszközöket és technikákat is. Végül átnéztük a párhuzamos programozással kapcsolatos legjobb programozási gyakorlatokat.

A cikk forráskódja megtalálható a GitHub oldalon.


$config[zx-auto] not found$config[zx-overlay] not found