A Grand Central Dispatch ( GCD ) az Apple technológiája olyan alkalmazások létrehozására, amelyek kihasználják a többmagos processzorok és más SMP - rendszerek előnyeit [1] . Ez a technológia a feladat párhuzamos megvalósítása, és a Thread Pool tervezési mintán alapul. A GCD-t először a Mac OS X 10.6 -tal vezették be . A GCD szolgáltatásokat megvalósító libdispatch könyvtár forráskódja 2009. szeptember 10-én jelent meg az Apache licenc alatt. [ 1] Archivált 2009. november 2-án a Wayback Machine -en . Ezt követően a könyvtárat áthelyezték [2] egy másik FreeBSD operációs rendszerre [3] .
A GCD lehetővé teszi, hogy párhuzamosan végrehajtható feladatokat definiáljunk egy alkalmazásban, és akkor futtassuk le ezeket, ha vannak szabad számítási erőforrások ( processzormagok ) [4] .
Egy feladat definiálható függvényként vagy " blokkként ". [5] A blokk a C / C++ / Objective-C programozási nyelvek nem szabványos szintaktikai kiterjesztése , amely a kódot és az adatokat egyetlen objektumba foglalja , hasonlóan a lezáráshoz . [négy]
A Grand Central Dispatch alacsony szinten használja a szálakat , de elrejti a megvalósítás részleteit a programozó elől. A GCD feladatok könnyűek, olcsón létrehozhatók és válthatók; Az Apple azt állítja, hogy egy feladat hozzáadásához a sorhoz mindössze 15 processzorutasítás szükséges , míg egy hagyományos szál létrehozása több száz utasításba kerül. [négy]
A GCD-feladattal olyan munkaelemet hozhatunk létre, amely egy feladatsorba kerül, vagy egy eseményforráshoz köthető. A második esetben, amikor az esemény elindul, a feladat bekerül a megfelelő sorba. Az Apple azt állítja, hogy ez a lehetőség hatékonyabb, mint egy külön szál létrehozása, amely az esemény aktiválására vár.
A GCD keretrendszer számos adattípust és függvényt deklarál azok létrehozásához és kezeléséhez.
A Grand Central Dispatch egyszerű használatára két példa található John Syracuse Snow Leopard áttekintésében az Ars Technicáról . [6] .
Kezdetben van egy olyan alkalmazásunk, amely egy analízisDocument metódussal rendelkezik, amely megszámolja a szavakat és a bekezdéseket a dokumentumban. Általában a szavak és bekezdések számlálása elég gyors, és a fő szálon elvégezhető anélkül, hogy félne attól, hogy a felhasználó késést észlel a gomb megnyomása és az eredmény megszerzése között:
- ( IBAction ) analysisDocument: ( NSButton * ) sender { NSDictionary * stats = [ myDoc elemzés ]; [ myModel setDict : stats ]; [ myStatsView setNeedsDisplay : YES ]; }Ha a dokumentum nagyon nagy, akkor az elemzés meglehetősen sokáig tarthat, amíg a felhasználó észreveszi az alkalmazás „lelógását”. A következő példa megkönnyíti a probléma megoldását:
- ( IBAction ) analysisDocument :( NSButton * ) sender { dispatch_async ( dispatch_get_global_queue ( 0 , 0 ), ^ { NSDictionary * stats = [ myDoc elemzés ]; dispatch_async ( dispatch_get_main_queue (), ^ { [ myModel setDict : stats ]; [ myStatsView setNeedsDisplay : YES ]; }); }); }Itt a [myDoc elemzés] hívás egy blokkba kerül, amely aztán az egyik globális sorba kerül. A [myDoc elemzés] befejezése után egy új blokk kerül a fő sorba, amely frissíti a felhasználói felületet . Ezekkel az egyszerű változtatásokkal a programozó elkerülte az alkalmazás esetleges lefagyását a nagy dokumentumok elemzésekor.
A második példa a hurok párhuzamosítását mutatja be:
for ( i = 0 ; i < count ; i ++ ) { eredmények [ i ] = do_work ( adat , i ); } összesen = összegez ( eredmények , szám );Itt a do_work függvényt count timesnak nevezzük , i-edik végrehajtásának eredményét az eredménytömb i-edik eleméhez rendeljük, majd az eredményeket összegezzük. Nincs okunk azt hinni, hogy a do_works a korábbi hívások eredményeire támaszkodik, így semmi akadálya annak, hogy párhuzamosan több do_works hívást hajtson végre. A következő lista bemutatja ennek az ötletnek a megvalósítását a GCD használatával:
dispatch_apply ( count , dispatch_get_global_queue ( 0 , 0 ), ^ ( size_t i ){ eredmények [ i ] = do_work ( adat , i ); }); összesen = összegez ( eredmények , szám );Ebben a példában a dispatch_apply lefutja a neki átadott blokk számát, minden hívást a globális sorba helyezve, és a blokkok számát 0-tól count-1-ig adja át . Ez lehetővé teszi az operációs rendszer számára, hogy kiválaszthassa az optimális számú szálat, hogy a lehető legtöbbet hozza ki a rendelkezésre álló hardvererőforrásokból. A dispatch_apply nem tér vissza mindaddig, amíg az összes blokkja be nem fejeződött, így biztosítva, hogy az összesítés meghívása előtt az eredeti ciklus összes munkája befejeződött.
A fejlesztő külön soros sort hozhat létre azokhoz a feladatokhoz, amelyeknek egymás után kell futniuk, de futhatnak külön szálon is. Új sor a következőképpen hozható létre:
dispatch_queue_t exampleQueue ; exampleQueue = dispatch_queue_create ( "com.example.unique.identifier" , NULL ); // exampleQueue itt használható. dispatch_release ( exampleQueue );Kerülje el, hogy egy ilyen feladatot olyan szekvenciális sorba helyezzen, amely egy másik feladatot helyez ugyanabba a sorba. Ez garantáltan holtponthoz vezet . Az alábbi felsorolás egy ilyen patthelyzetet mutat be:
dispatch_queue_t exampleQueue = dispatch_queue_create ( "com.example.unique.identifier" , NULL ); dispatch_sync ( exampleQueue , ^ { dispatch_sync ( exampleQueue , ^ { printf ( "Most holtpontra jutottam... \n " ); }); }); dispatch_release ( exampleQueue );