ebben az oktatóanyagban megvizsgáljuk, hogyan használhatja a csatornákat a Go-alapú alkalmazásokban.
a csatornák olyan csövek, amelyek összekapcsolják a goroutines
között a Go basedapplications-en belül, amelyek lehetővé teszik a kommunikációt, majd az értékek átadását a változókból.
ezek hihetetlenül praktikus,és segít kézműves hihetetlenül nagy teljesítményű, nagyon egyidejű alkalmazások Go minimális felhajtás képest más programozási nyelvek. Ez egyáltalán nem volt véletlen, a nyelv megtervezésekor a fő fejlesztők úgy döntöttek, hogy azt akarják, hogy a nyelvükön belüli párhuzamosság első osztályú állampolgár legyen, és hogy a lehető legegyszerűbbé tegyék a munkát, anélkül, hogy túl messzire mennének, és nem engednék meg a fejlesztőknek azt a szabadságot, amelyben dolgozniuk kell.
az a képesség, hogy az egyidejű rendszereket ilyen könnyen el lehet készíteni, elsősorban a nyelv vonzotta, és azt kell mondanom, ez eddig abszolút öröm volt.
megjegyzés – azt javasoljuk, hogy vessen egy pillantást a többi bemutató ongoroutines ha szeretné, hogy learnmore körülbelül goroutines.
célok
a bemutató végére:
- van egy szilárd megértése, hogy az elmélet mögött csatornák
- legyen képes létrehozni egyszerű egyidejű Go alkalmazásokat használó csatornák
előfeltételek
annak érdekében, hogy teljes ez a bemutató, akkor meg kell felelnie a következő előfeltételei:
- szüksége lesz Go telepítve a gépen.
videó bemutató
ha szeretné, ez a bemutató elérhető videó formátumban.
az elmélet
a csatornák gondolata nem új keletű, mivel a GO sok egyidejűségjellemzőjéhez hasonlóan ezeket a fogalmakat a Hoare ‘s communicating Sequential Processes (1978), röviden CSP, sőt a Dijkstra’ s őrzött parancsok (1975).
a GO fejlesztői azonban küldetésükké tették, hogy ezeket a fogalmakat a lehető legegyszerűbb módon mutassák be, hogy lehetővé tegyék a programozók számára, hogy jobb, helyesebb, erősen párhuzamos alkalmazásokat hozzanak létre.
egyszerű példa
kezdjük azzal, hogy megnézzük, hogyan építhetünk fel egy nagyon egyszerű példát arra, hogy ez hogyan működik a Go-ban. Először létrehozunk egy függvényt, amely eltűnik, kiszámítja az anarbitrary, random értéket, majd visszaadja egy csatorna változónakvalues
:
vizsgáljuk meg, mi történt itt. A mi main()
funkció, hívtukvalues := make(chan int)
, ez a hívás hatékonyan létre az új csatorna sothat tudtuk később használni belül a CalculateValue
goroutine.
megjegyzés – a
make
– ot használtuk avalues
csatornánk példányosításakor, mivel a likemaps és slices csatornákat használat előtt létre kell hozni.
miután létrehoztuk a csatornát, felhívtuk az defer close(values)
– et, amely korlátozta csatornánk bezárását a main()
funkció végrehajtásának végéig. Ezt általában a legjobb gyakorlatnak tekintik annak biztosítására, hogy rendet tegyünk magunk után.
miután felhívtuk a defer
– et, elindítjuk az egyetlen goroutine-t:CalculateValue(values)
az újonnan létrehozott values
csatornánkon áthaladva itsparameterként. A CalculateValue
függvényen belül kiszámítunk egy véletlenszerű értéket 1-10 között, kinyomtatjuk ezt, majd elküldjük ezt az értéket a values
csatornánknak a values <- value
hívásával.
visszaugrunk a main()
funkciónkba, majd meghívjuk a value := <-values
– ot, amely értéket kap a values
csatornánkból.
Megjegyzés – figyelje meg, hogy amikor végrehajtjuk ezt a programot, akkor nem azonnal véget ér. Ez azért van, mert a csatorna küldésének és fogadásának aktusa blokkolva van. A
main()
függvény blokkol, amíg nem kap értéket a ourchannel – től.
ennek a kódnak a végrehajtásakor látnia kell, hogy a kimenet valahogy így néz ki:
$ go run main.goGo Channel TutorialCalculated Random Value: {} 77
összefoglaló:
myChannel := make(chan int)
– létrehozza a myChannel-t, amely egy típusú csatornaint
channel <- value
– értéket küld egy csatornának
value := <- channel
– értéket kap egy csatornától
tehát a csatornák példányosítása és használata a Go programokban eddig meglehetősen egyszerűnek tűnik, de mi a helyzet a bonyolultabb forgatókönyvekkel?
nem pufferelt csatornák
a hagyományos channel
használata a goroutinokon belül néha olyan viselkedéshez vezethet, amelyre nem biztos, hogy számít. A hagyományosunbuffered
csatornáknál, amikor egy goroutine értéket küld erre a csatornára,akkor a goroutine ezt követően blokkolja, amíg az érték meg nem érkezik a csatornától.
lássuk ezt egy valós példában. Ha megnézzük az alábbi kódot, akkor nagyonhasonlóan a korábban használt kódhoz. Azonban kiterjesztettük aCalculateValue()
függvényünket egy fmt.Println
végrehajtására, miután elküldte a véletlenszerűen kiszámított értéket a csatornának.
a mi main()
függvény, már hozzá egy második hívástgo CalculateValue(valueChannel)
tehát meg kell várni 2 értékek küldött thischannel nagyon gyors egymásutánban.
ennek futtatásakor azonban látnia kell, hogy csak az első goroutines finalprint utasításunk kerül végrehajtásra:
go run main.goGo Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 71Only Executes after another goroutine performs a receive on the channel
ennek az az oka, hogy a hívás c <- value
blokkolta a mi secondgorutine és ezt követően a main()
függvény arra a következtetésre jut, hogy a végrehajtás előtt a második goroutine
kap egy esélyt, hogy befejezze a saját végrehajtását.
pufferelt csatornák
a blokkoló viselkedés megkerülésének módja az úgynevezett abuffered channel használata. Ezek a pufferelt csatornák lényegében egy adott méretű sorok, amelyek felhasználhatók a goroutin közötti kommunikációhoz. Annak érdekében, hogy abuffered csatornát hozzunk létre, szemben a nem pufferelt csatornával, kapacitási argumentumot adunk a make
parancshoz:
bufferedChannel := make(chan int, 3)
ha ezt pufferelt csatornára változtatjuk, a küldési műveletünk c <- value
onlyblocks a goroutinjainkon belül, ha a csatorna megtelt.
módosítsuk meglévő programunkat egy pufferelt csatorna használatára, és nézzük meg a kimenetet. Figyeljük meg, hogy már hozzá egy hívást time.Sleep()
alján amain()
funkció annak érdekében, hogy lustán blokkolja a main()
funkció elég allowour goroutines teljes végrehajtását.
Most, amikor ezt végrehajtjuk, látnunk kell, hogy a második gorutinunk valóban folytatja a végrehajtását, függetlenül attól, hogy a második fogadás nem volt hívva a main()
függvényünkben. A time.Sleep()
-nek köszönhetően világosan láthatjuk a különbséget a nem pufferelt csatornák és azok blokkoló jellege, valamint a mi pufferelt csatornáink és azok nem blokkoló (ha nem teljes) jellege között.
Go Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 77This executes regardless as the send is now non-blockingThis executes regardless as the send is now non-blocking
következtetés
tehát ebben a meglehetősen hosszú oktatóanyagban sikerült megismerkednünk a Go különböző csatornatípusaival. Felfedeztük a különbségeket mind a pufferelt, mind a nem pufferelt csatornák között, és hogyan használhatnánk őket előnyünkre a párhuzamos go programjainkban.
ha tetszett ez a bemutató, akkor nyugodtan tudassa velem az alábbi megjegyzések részben. Ha bármilyen javaslata van arról, hogy mit tehetnék jobban, akkor szeretném hallani őket az alábbi megjegyzések részben!
további olvasmányok
ha tetszett ez a cikk, és szeretne többet megtudni a Concurrencyin Go – val való együttműködésről, akkor azt javaslom, hogy nézze meg a konkurenciáról szóló többi cikkünket:
- Go Mutex bemutató
- Go Goroutines bemutató
- Go szinkronizálás.WaitGroup Bemutató