Grand Central Dispatch ( GCD ) on Applen tekniikka moniytimisprosessoreja ja muita SMP - järjestelmiä hyödyntävien sovellusten rakentamiseen [1] . Tämä tekniikka on tehtävien rinnakkaisuuden toteutus ja perustuu Thread Pool - suunnittelumalliin . GCD esiteltiin ensimmäisen kerran Mac OS X 10.6 :n kanssa . GCD-palvelut toteuttavan libdispatch- kirjaston lähdekoodi julkaistiin Apache-lisenssillä 10. syyskuuta 2009. [ 1] Arkistoitu 2. marraskuuta 2009 Wayback Machinessa . Myöhemmin kirjasto siirrettiin [2] toiseen FreeBSD - käyttöjärjestelmään [3] .
GCD:n avulla voit määrittää sovelluksessa tehtäviä, jotka voidaan suorittaa rinnakkain, ja suorittaa ne, kun on vapaita laskentaresursseja ( prosessoriytimiä ) [4] .
Tehtävä voidaan määritellä funktioksi tai " lohkoksi ". [5] Lohko on epästandardi C / C++ / Objective-C- ohjelmointikielten syntaksilaajennus , joka kapseloi koodin ja datan yhdeksi objektiksi sulkemisen tapaan . [neljä]
Grand Central Dispatch käyttää säikeitä matalalla tasolla, mutta piilottaa toteutustiedot ohjelmoijalta. GCD-tehtävät ovat kevyitä, edullisia luoda ja vaihtaa; Apple väittää, että tehtävän lisääminen jonoon vaatii vain 15 prosessoriohjetta , kun taas perinteisen säikeen luominen maksaa useita satoja ohjeita. [neljä]
GCD-tehtävän avulla voidaan luoda työkohde, joka sijoitetaan tehtäväjonoon tai joka voidaan sitoa tapahtumalähteeseen. Toisessa tapauksessa, kun tapahtuma käynnistyy, tehtävä lisätään sopivaan jonoon. Apple väittää, että tämä vaihtoehto on tehokkaampi kuin erillisen säikeen luominen odottamaan tapahtuman käynnistymistä.
GCD-kehys ilmoittaa useita tietotyyppejä ja toimintoja niiden luomiseksi ja käsittelemiseksi.
Kaksi esimerkkiä, jotka osoittavat Grand Central Dispatchin helppokäyttöisyyden, löytyvät John Syracusen Snow Leopard -katsauksesta Ars Technicasta . [6] .
Aluksi meillä on sovellus analysoidaDocument-menetelmällä, joka laskee sanat ja kappaleet asiakirjassa. Yleensä sanojen ja kappaleiden laskentaprosessi on riittävän nopea ja se voidaan tehdä pääsäikeessä ilman pelkoa, että käyttäjä huomaa viiveen painikkeen painamisen ja tuloksen saamisen välillä:
- ( IBAction ) analysoiDokumentti: ( NSButton * ) sender { NSDictionary * stats = [ myDoc analysoida ]; [ myModel setDict : tilastot ]; [ myStatsView setNeedsDisplay : YES ]; }Jos dokumentti on erittäin suuri, analyysi voi kestää melko kauan ennen kuin käyttäjä huomaa sovelluksen "roikkumisen". Seuraava esimerkki helpottaa tämän ongelman ratkaisemista:
- ( IBAction ) analysoiDocument :( NSButton * ) sender { dispatch_async ( dispatch_get_global_queue ( 0 , 0 ), ^ { NSDictionary * stats = [ myDoc analysoida ]; dispatch_async ( dispatch_get_main_queue (), ^ { [ myModel setDict : tilastot ]; [ myStatsView setNeedsDisplay : YES ]; }); }); }Tässä [myDoc-analyysi]-kutsu sijoitetaan lohkoon, joka sitten sijoitetaan johonkin globaaleista jonoista. Kun [myDoc-analyysi] on valmis, pääjonoon asetetaan uusi lohko, joka päivittää käyttöliittymän . Tekemällä nämä yksinkertaiset muutokset ohjelmoija vältti sovelluksen mahdollisen jumittumisen suuria asiakirjoja jäsennettäessä.
Toinen esimerkki osoittaa silmukan rinnakkaisuuden:
for ( i = 0 ; i < count ; i ++ ) { tulokset [ i ] = tee_työ ( data , i ); } yhteensä = yhteenveto ( tulokset , laske );Tässä do_work-funktiota kutsutaan count times, sen i:nnen suorituksen tulos osoitetaan tulostaulukon i:nnelle elementille, minkä jälkeen tulokset lasketaan yhteen. Ei ole mitään syytä uskoa, että do_works luottaisi siihen aiempien kutsujen tuloksiin, joten mikään ei estä tekemästä useita do_works-kutsuja rinnakkain. Seuraava luettelo osoittaa tämän idean toteuttamisen GCD:tä käyttämällä:
dispatch_apply ( count , dispatch_get_global_queue ( 0 , 0 ), ^ ( size_t i ){ tulokset [ i ] = tee_työ ( data , i ); }); yhteensä = yhteenveto ( tulokset , laske );Tässä esimerkissä dispatch_apply ajaa sille välitetyn lohkon laskentakertoja ja asettaa jokaisen kutsun globaaliin jonoon ja välittää lohkojen numerot nollasta count-1:een. Tämän ansiosta käyttöjärjestelmä voi valita optimaalisen säikeiden määrän saadakseen parhaan hyödyn käytettävissä olevista laitteistoresursseista. dispatch_apply ei palauta ennen kuin kaikki sen lohkot ovat valmiita, jotta varmistetaan, että kaikki alkuperäisen silmukan työ on suoritettu ennen yhteenvedon kutsumista.
Kehittäjä voi luoda erillisen sarjajonon tehtäville, joiden pitäisi ajaa peräkkäin, mutta jotka voivat ajaa erillisessä säikeessä. Uusi jono voidaan luoda seuraavasti:
dispatch_queue_t exampleQueue ; exampleQueue = dispatch_queue_create ( "com.example.uique.identifier" , NULL ); // esimerkkijonoa voidaan käyttää tässä. dispatch_release ( exampleQueue );Vältä tällaisen tehtävän asettamista peräkkäiseen jonoon, joka asettaa toisen tehtävän samaan jonoon. Tämä johtaa taatusti umpikujaan . Seuraava luettelo osoittaa tällaisen umpikujan tapauksen:
dispatch_queue_t exampleQueue = lähetysjono_luo ( "com.example.uique.identifier" , NULL ); dispatch_sync ( exampleQueue , ^ { dispatch_sync ( exampleQueue , ^ { printf ( "Olen nyt umpikujassa... \n " ); }); }); dispatch_release ( exampleQueue );