Grand Central Dispatch ( GCD ) je technologie společnosti Apple pro vytváření aplikací, které využívají výhod vícejádrových procesorů a dalších systémů SMP [1] . Tato technologie je implementací paralelismu úloh a je založena na návrhovém vzoru fondu vláken. GCD byl poprvé představen s Mac OS X 10.6 . Zdrojový kód knihovny libdispatch , která implementuje služby GCD, byl vydán pod licencí Apache dne 10. září 2009. [ 1] Archivováno 2. listopadu 2009 na Wayback Machine . Následně byla knihovna portována [2] na jiný operační systém FreeBSD [3] .
GCD umožňuje definovat úlohy v aplikaci, které lze spouštět paralelně, a spouští je, když jsou volné výpočetní zdroje ( jádra procesoru ) [4] .
Úkol může být definován jako funkce nebo jako " blok ". [5] Blok je nestandardní rozšíření syntaxe programovacích jazyků C / C++ / Objective-C , které zapouzdřuje kód a data do jednoho objektu, analogicky k uzávěru . [čtyři]
Grand Central Dispatch používá vlákna na nízké úrovni, ale skrývá detaily implementace před programátorem. Úlohy GCD jsou lehké, nenákladné na vytváření a přepínání; Apple tvrdí, že přidání úlohy do fronty vyžaduje pouze 15 instrukcí procesoru , zatímco vytvoření tradičního vlákna stojí několik stovek instrukcí. [čtyři]
Úlohu GCD lze použít k vytvoření pracovní položky, která je umístěna ve frontě úloh nebo může být svázána se zdrojem události. V druhém případě, když dojde k události, je úloha přidána do příslušné fronty. Apple tvrdí, že tato možnost je efektivnější než vytvoření samostatného vlákna čekajícího na spuštění události.
Rámec GCD deklaruje několik datových typů a funkcí pro jejich vytváření a manipulaci.
Dva příklady demonstrující snadnost použití Grand Central Dispatch lze nalézt v recenzi Snow Leoparda Johna Syracuse na Ars Technica . [6] .
Zpočátku máme aplikaci s metodou analysisDocument, která počítá slova a odstavce v dokumentu. Obvykle je proces počítání slov a odstavců dostatečně rychlý a lze jej provést v hlavním vláknu bez obav, že uživatel zaznamená zpoždění mezi stisknutím tlačítka a získáním výsledku:
- ( IBAction ) analyzovatDokument: ( NSButton * ) odesílatel { NSDictionary * stats = [ myDoc analysis ] ; [ myModel setDict : statistiky ]; [ myStatsView setNeedsDisplay : YES ]; }Pokud je dokument velmi velký, může analýza trvat poměrně dlouho, než si uživatel všimne „zasekávání“ aplikace. Následující příklad usnadňuje řešení tohoto problému:
- ( IBAction ) analyzovatDokument :( NSButton * ) odesílatel { dispatch_async ( dispatch_get_global_queue ( 0 , 0 ), ^ { NSDictionary * stats = [ myDoc analysis ] ; dispatch_async ( dispatch_get_main_queue (), ^ { [ myModel setDict : statistiky ]; [ myStatsView setNeedsDisplay : YES ]; }); }); }Zde je volání [myDoc analysis] umístěno do bloku, který je pak umístěn do jedné z globálních front. Po dokončení [analýza myDoc] se do hlavní fronty umístí nový blok, který aktualizuje uživatelské rozhraní . Provedením těchto jednoduchých změn se programátor vyhnul potenciálnímu zablokování aplikace při analýze velkých dokumentů.
Druhý příklad ukazuje paralelizaci smyčky:
for ( i = 0 ; i < počet ; i ++ ) { výsledky [ i ] = do_work ( data , i ); } celkem = shrnout ( výsledky , počet );Zde se funkce do_work nazývá count times, výsledek jejího i-tého provedení se přiřadí i-tému prvku pole výsledků, poté se výsledky sečtou. Není důvod se domnívat, že do_works spoléhá na výsledky předchozích volání, takže nic nebrání paralelnímu provádění několika volání do_works. Následující seznam ukazuje implementaci této myšlenky pomocí GCD:
dispatch_apply ( count , dispatch_get_global_queue ( 0 , 0 ), ^ ( size_t i ){ výsledky [ i ] = do_work ( data , i ); }); celkem = shrnout ( výsledky , počet );V tomto příkladu spustí dispatch_apply počet , kolikrát mu byl předán blok, přičemž každé volání umístí do globální fronty a předá čísla bloků od 0 do počtu-1. To umožňuje OS vybrat optimální počet vláken, aby bylo možné co nejlépe využít dostupné hardwarové prostředky. dispatch_apply se nevrátí, dokud nejsou dokončeny všechny jeho bloky, aby bylo zajištěno, že veškerá práce původní smyčky byla dokončena před voláním sum.
Vývojář může vytvořit samostatnou sériovou frontu pro úlohy, které by se měly spouštět postupně, ale mohou běžet v samostatném vláknu. Novou frontu lze vytvořit takto:
dispatch_queue_t exampleQueue ; exampleQueue = dispatch_queue_create ( "com.example.unique.identifier" , NULL ); // zde lze použít exampleQueue. dispatch_release ( exampleQueue );Vyhněte se umístění takové úlohy do sekvenční fronty, která zařadí jinou úlohu do stejné fronty. To zaručeně povede k uváznutí . Následující výpis ukazuje případ takového zablokování:
dispatch_queue_t exampleQueue = dispatch_queue_create ( "com.example.unique.identifier" , NULL ); dispatch_sync ( exampleQueue , ^ { dispatch_sync ( exampleQueue , ^ { printf ( "Nyní jsem uvízl... \n " ); }); }); dispatch_release ( exampleQueue );