Kuva kuuluisasta

Samanaikaisuus vs. tapahtumasilmukka vs. tapahtumapiiri + samanaikaisuus

Ensinnäkin selitetään terminologiaa.
Samanaikaisuus - tarkoittaa, että sinulla on useita tehtäväjonoja useilla suorittimen ytimillä / säieillä. Mutta se on täysin erilainen kuin Rinnakkaissuoritus, rinnakkaissuoritus ei sisältäisi useita tehtäväjonoja Rinnakkaisissa tapauksissa. Tarvitsemme yhden prosessorin ytimen / säiettä kutakin tehtävää varten täydellisen rinnakkaissuorituksen toteuttamiseksi, jota useimmissa tapauksissa emme voi määritellä. Siksi modernille ohjelmistokehitykselle rinnakkaisohjelmointi tarkoittaa joskus “samanaikaisuutta”, tiedän, että se on outoa, mutta selvästi se on mitä meillä tällä hetkellä on (se riippuu OS cpu / thread-mallista).
Tapahtumasilmukka - Tarkoittaa yhden kierteitetyn äärettömän jakson, joka tekee yhden tehtävän kerrallaan ja joka ei ole vain yhden tehtäväjonon luominen, vaan myös priorisoi tehtäviä, koska tapahtumasilmukalla sinulla on vain yksi resurssi suorittamiseen (1 säie), joten suorittamiseen joitain tehtäviä heti, sinun on priorisoitava tehtävät. Joidenkin sanoen tätä ohjelmointitapaa kutsutaan langanpitäväksi ohjelmointiin, koska vain yksi tehtävä / toiminto / toimenpide voidaan suorittaa kerrallaan, ja jos muutat jotain, se muuttuisi jo seuraavan tehtävän suorittamisen aikana.

Samanaikainen ohjelmointi

Nykyaikaisissa tietokoneissa / palvelimissa meillä on vähintään 2 CPU-ydintä ja min. 4 prosessorin säiettä. Mutta palvelimilla nyt keskim. palvelimella on vähintään 16 CPU-ketjua. Joten jos kirjoitat ohjelmistoa, joka tarvitsee jonkin verran suorituskykyä, sinun kannattaa ehdottomasti harkita sen tekemistä siten, että se käyttää kaikkia palvelimella olevia CPU-ytimiä.

Tämä kuva näyttää samanaikaisuuden perusmallin, mutta corse-malli ei ole niin helppoa, että se näytetään :)

Samanaikaisuuden ohjelmoinnista on tulossa todella vaikeaa joidenkin jaettujen resurssien avulla, esimerkiksi tarkastellaan tätä Go yksinkertaista samanaikaista koodia.

// Väärä samanaikaisuus Go-kielen kanssa
paketin pää
tuonti (
   "FMT"
   "aika"
)
var SharedMap = merkki (kartta [merkkijono] merkkijono)
func changeMap (arvojono) {
    SharedMap ["test"] = arvo
}
func main () {
    siirry muutoskarttaan ("arvo1")
    siirry muutoskarttaan ("arvo2")
    aika.Majoitus (aika.Millisekuntia * 500)
    fmt.Println (SharedMap [ "test"])
}
// Tulostetaan "arvo1" tai "arvo2", jota emme tiedä tarkalleen!

Tässä tapauksessa Go potkaisee 2 samanaikaista työtä todennäköisesti erilaisille CPU-ytimille, emmekä voi ennustaa, mikä niistä suoritetaan ensin, joten emme tiedä, mitä lopussa näytetään.
Miksi? - Se on yksinkertaista! Ajoitamme 2 erilaista tehtävää eri CPU-ytimille, mutta ne käyttävät yhtä jaettua muuttujaa / muistia, joten ne molemmat muuttavat muistia, ja joissain tapauksissa tämä tapahtuu ohjelman kaatumisen / poikkeuksen tapauksessa.

Joten samanaikaisuuden ohjelmoinnin suorituksen ennustamiseksi meidän on käytettävä joitain lukitustoimintoja, kuten Mutex. Sen avulla voimme lukita jaetun muistin resurssin ja asettaa sen saataville vain yhtä tehtävää kerrallaan.
Tämä ohjelmointityyli nimeltään Estäminen, koska estämme tosiasiassa kaikki tehtävät, kunnes nykyinen tehtävä suoritetaan jaetulla muistilla.

Suurin osa kehittäjistä ei pidä samanaikaisesta ohjelmoinnista, koska samanaikaisuus ei aina tarkoita suorituskykyä. Se riippuu erityistapauksista.

Yksisäikeinen tapahtumasilmukka

Tämä ohjelmistokehitysmalli on paljon yksinkertaisempi kuin samanaikainen ohjelmointi. Koska periaate on hyvin yksinkertainen. Sinulla on vain yksi tehtävän suorittaminen kerrallaan. Ja tässä tapauksessa sinulla ei ole ongelmia jaettujen muuttujien / muistin kanssa, koska ohjelma on paremmin ennustettavissa yksittäisellä tehtävällä kerrallaan.

Yleinen virtaus seuraa
1. Tapahtumapäästö lisää tehtävää tapahtumajonoon, joka suoritetaan seuraavalla silmukkijaksolla
2. Event Loop -tehtävän saaminen tapahtumajonosta ja prosessointi käsittelijöiden perusteella

Annetaan kirjoittaa sama esimerkki node.js: n kanssa

anna SharedMap = {};
const changeMap = (arvo) => {
    return () => {
        SharedMap ["test"] = arvo
    }
}
// 0 Aikakatkaisu tarkoittaa, että olemme tekemässä uutta tehtävää jonossa seuraavalle jaksolle
setTimeout (changeMap ("arvo1"), 0);
setTimeout (changeMap ("arvo2"), 0);
setTimeout (() => {
   console.log (SharedMap [ "test"])
}, 500);
// tässä tapauksessa Node.js tulostaa "arvo2", koska se on yksi
// kierretty ja siinä on "vain yksi tehtäväjono"

Kuten voitte kuvitella, tässä tapauksessa on koodin tapa ennustettavissa kuin samanaikaisen Go-esimerkin kanssa, ja se johtuu siitä, että Node.js toimii yhdessä kierretilassa käyttämällä JavaScript-tapahtumasilmukkaa.

Joissain tapauksissa tapahtumasilmukka antaa enemmän suorituskykyä kuin samanaikaisesti, estämättömän käyttäytymisen vuoksi. Erittäin hyvä esimerkki on verkkoratkaisusovellukset, koska ne käyttävät yksittäistä verkkoyhteysresurssia ja käsittelevät tietoja vain silloin, kun se on saatavilla Thread Safe Event Loops -silmukoilla.

Samanaikaisuus + Tapahtumasilmukka - Kiertoallas langansuojauksella

Sovellusten tekeminen vain samanaikaisesti voi olla erittäin haastavaa, koska muistin vioittumisvirheitä on kaikkialla tai yksinkertaisesti sovelluksesi alkaa estää toimintoja jokaisessa tehtävässä. Varsinkin jos haluat saada maksimaalisen suorituskyvyn, sinun on yhdistettävä molemmat!

Katsotaanpa Thread Pool + Event Loop -malli Nginx Web Server Structure -sovelluksesta

Tärkeimmän verkottumisen ja määrityskäsittelyn tekee Worker Event Loop yhtenä säikeenä turvallisuuden vuoksi, mutta kun Nginxin on luettava tiedosto tai käsiteltävä HTTP-pyyntöotsikot / runko, jotka estävät toiminnot, se lähettää kyseisen tehtävän hänen ketjuunsa samanaikaiseen käsittelyyn. Ja kun tehtävä on suoritettu, tulos lähetetään takaisin tapahtumasilmukalle suoritetun tuloksen säiettä varten.

Joten käyttämällä tätä rakennetta, saat sekä säieturvallisuuden että samanaikaisuuden, mikä sallii kaikkien suorittimen ytimien käytön suorituskyvyn kannalta ja estää estämättömän periaatteen yhdellä kierteitetyllä tapahtumasilmukalla.

johtopäätös

Paljon ohjelmistoja kirjoitetaan puhtaalla samanaikaisuudella tai puhtaalla yksisäikeisellä tapahtumasilmukalla, mutta yhdistämällä molemmat sisällä yhden sovelluksen, mikä helpottaa suorittavien sovellusten kirjoittamista ja kaikkien käytettävissä olevien CPU-resurssien käyttöä.