- Bevezetés
- Szállítási Garancia
- állapot
- adatokkal és anélkül
- jelzés adatokkal
- adat nélküli jelzés
- forgatókönyvek
- jel Adatgarancia – puffer nélküli csatornákkal
- jel adatokkal-nincs garancia-pufferelt csatornák >1
- adatjel – késleltetett garancia – pufferelt 1.csatorna
- Signal without Data – Context
- következtetés
Bevezetés
amikor először kezdtem el dolgozni a Go csatornáival, elkövettem azt a hibát, hogy a csatornákra mint adatstruktúrára gondoltam. A csatornákat olyan sorként láttam, amely automatikus szinkronizált hozzáférést biztosított a goroutines között. Ez a strukturális megértés sok rossz és bonyolult egyidejű kódot írt.
az idő múlásával megtanultam, hogy a legjobb, ha elfelejtjük a csatornák felépítését, és arra koncentrálunk, hogyan viselkednek. Tehát most, amikor a csatornákról van szó, egy dologra gondolok: jelzés. A csatorna lehetővé teszi az egyik goroutine számára, hogy jelezzen egy másik goroutine-t egy adott eseményről. A jelzés a lényege mindennek, amit a csatornákkal kell tennie. A csatornákra, mint jelzőmechanizmusra gondolva jobb kódot írhat jól definiált és pontosabb viselkedéssel.
ahhoz, hogy megértsük, hogyan működik a jelzés, meg kell értenünk annak három tulajdonságát:
- Szállítási Garancia
- állapot
- adatokkal vagy anélkül
ez a három attribútum együtt dolgozik, hogy létrehozzon egy tervezési filozófiát a jelzés körül. Miután megvitattam ezeket az attribútumokat, számos olyan kódpéldát fogok bemutatni, amelyek bemutatják a jelzést ezekkel az attribútumokkal.
Szállítási Garancia
a Szállítási Garancia egy kérdésen alapul: “szükségem van garanciára, hogy egy adott goroutine által küldött jel érkezett?”
más szóval, ezt a példát az 1. listában:
lista 1
01 go func() {02 p := <-ch // Receive03 }()0405 ch <- "paper" // Send
szüksége van-e a küldő goroutine-nak garanciára, hogy a paper
– et a csatornán keresztül a 05-ös vonalon küldték el a goroutine a 02-es vonalon, mielőtt továbblépne?
a kérdésre adott válasz alapján tudni fogja, hogy a kétféle csatorna közül melyiket használja: nem pufferelt vagy pufferelt. Minden csatorna eltérő viselkedést biztosít a kézbesítési garanciák körül.
1.ábra : Szállítási Garancia
a garanciák fontosak, és ha nem gondolja, van egy csomó dolog, amit el akarok adni neked. Természetesen próbálok viccelődni, de nem ideges, ha nincs garancia az életben? Miután egy erős megértése, hogy szükség van-e a garancia elengedhetetlen írásakor egyidejű szoftver. Ahogy folytatjuk, megtanulod, hogyan kell dönteni.
állapot
a csatorna viselkedését közvetlenül befolyásolja a jelenlegi állapota. A csatorna állapota lehet nulla, nyitott vagy zárt.
az alábbi 2.lista bemutatja, hogyan lehet csatornát deklarálni vagy elhelyezni e három állapot mindegyikébe.
felsorolás 2
// ** nil channel// A channel is in a nil state when it is declared to its zero valuevar ch chan string// A channel can be placed in a nil state by explicitly setting it to nil.ch = nil// ** open channel// A channel is in a open state when it's made using the built-in function make.ch := make(chan string) // ** closed channel// A channel is in a closed state when it's closed using the built-in function close.close(ch)
az állapot határozza meg, hogyan viselkednek a küldés és fogadás műveletek.
a jeleket egy csatornán keresztül küldik és fogadják.
2.ábra : állapot
amikor egy csatorna nulla állapotban van, a csatornán megkísérelt küldés vagy fogadás blokkolódik. Amikor egy csatorna nyitott állapotban van, jeleket lehet küldeni és fogadni. Amikor egy csatorna zárt állapotba kerül, a jelek már nem küldhetők, de a jelek fogadása továbbra is lehetséges.
ezek az állapotok biztosítják a különböző viselkedéseket, amelyekre szüksége van a különböző helyzetekben. Ha az államot a szállítási garanciával kombinálja, elkezdheti elemezni a tervezési döntések eredményeként felmerülő költségeket/előnyöket. Sok esetben a kód elolvasásával gyorsan észlelheti a hibákat is, mert megérti, hogyan fog viselkedni a csatorna.
adatokkal és anélkül
az utolsó jelátviteli attribútum, amelyet figyelembe kell venni, az, hogy adatokkal vagy anélkül kell-e jelezni.
jelzéssel adatok végrehajtásával egy send egy csatornán.
felsorolás 3
01 ch <- "paper"
amikor adatokkal jelez, ez általában azért van, mert:
- egy gorutint kérnek egy új feladat elindítására.
- egy gorutin jelentést készít egy eredményről.
a csatorna bezárásával adat nélkül jelezhet.
Felsorolás 4
01 close(ch)
amikor adat nélkül jelez, általában azért van, mert:
- a gorutin azt mondják, hogy hagyja abba, amit csinálnak.
- a goroutine beszámol arról, hogy eredmény nélkül végeztek.
- egy gorutin azt jelenti, hogy befejezte a feldolgozást és leállt.
vannak kivételek e szabályok alól, de ezek a legfontosabb felhasználási esetek, amelyekre ebben a bejegyzésben összpontosítunk. E szabályok alóli kivételeket kezdeti kódszagnak tekinteném.
az adatok nélküli jelzés egyik előnye, hogy egyetlen goroutin egyszerre sok goroutint képes jelezni. Az adatokkal történő jelzés mindig 1-1-es csere a goroutinok között.
jelzés adatokkal
ha adatokkal kíván jelezni, három csatornakonfigurációs lehetőség közül választhat a szükséges garancia típusától függően.
3. ábra : Jelzés adatokkal
a három csatorna opció nem pufferelt, pufferelt >1 vagy pufferelt =1.
-
garancia
- a nem pufferelt csatorna garantálja, hogy a küldött jel érkezett.
- mert a jel vétele a jel küldésének befejezése előtt történik.
- a nem pufferelt csatorna garantálja, hogy a küldött jel érkezett.
-
nincs garancia
- a > 1 méretű pufferelt csatorna nem garantálja, hogy a küldött jel érkezett.
- mert a jel küldése a jel vétele előtt történik.
- a > 1 méretű pufferelt csatorna nem garantálja, hogy a küldött jel érkezett.
-
késleltetett garancia
- a =1 méretű pufferelt csatorna késleltetett garanciát ad. Garantálhatja, hogy az előző küldött jel érkezett.
- mivel az első jel vétele még a második jel küldése előtt megtörténik.
- a =1 méretű pufferelt csatorna késleltetett garanciát ad. Garantálhatja, hogy az előző küldött jel érkezett.
a puffer mérete soha nem lehet véletlen szám, mindig ki kell számítani valamilyen jól meghatározott kényszerre. A számítástechnikában nincs végtelen, mindennek rendelkeznie kell valamilyen jól meghatározott korlátozással, legyen az idő vagy tér.
adat nélküli jelzés
az adat nélküli jelzés elsősorban törlésre van fenntartva. Ez lehetővé teszi, hogy az egyik goroutine jelezzen egy másik goroutine-nak, hogy törölje azt, amit csinál, és lépjen tovább. A Törlés mind puffer nélküli, mind pufferelt csatornákkal megvalósítható, de pufferelt csatorna használata, ha nem küldünk adatokat, kódszag.
4. ábra : Jelzés adatok nélkül
a close
beépített funkció adat nélküli jelzésre szolgál. Amint azt az állapot szakaszban fentebb kifejtettük, továbbra is fogadhat jeleket egy zárt csatornán. Valójában a zárt csatornán történő fogadás nem fog blokkolni, és a vételi művelet mindig visszatér.
a legtöbb esetben a standard library context
csomagot szeretné használni az adatok nélküli jelzés megvalósításához. A context
csomag alatta egy nem pufferelt csatornát használ a jelzéshez, a beépített close
funkció pedig adat nélküli jelzéshez.
ha úgy dönt, hogy a saját csatornáját használja a törléshez a kontextuscsomag helyett, akkor a csatornájának chan struct{}
típusúnak kell lennie. Ez a nulla tér, idiomatikus módszer a csak jelzésre használt csatorna jelzésére.
forgatókönyvek
ezekkel az attribútumokkal a legjobb módja annak, hogy jobban megértsük, hogyan működnek a gyakorlatban, ha végigfuttatunk egy sor kódforgatókönyvet. Szeretek a goroutinokra emberként gondolni, amikor csatorna alapú kódot olvasok és írok. Ez a vizualizáció valóban segít, és az alábbiakban segédeszközként fogom használni.
jel Adatgarancia – puffer nélküli csatornákkal
ha tudnia kell, hogy egy küldött jel érkezett, két forgatókönyv jön létre. Ezek várnak a feladatra és várnak az eredményre.
1.forgatókönyv – várjon a feladatra
gondoljon arra, hogy menedzser lesz, és új alkalmazottat vesz fel. Ebben az esetben azt szeretné, hogy az új alkalmazott elvégezzen egy feladatot, de meg kell várnia, amíg készen áll. Ez azért van, mert meg kell adni nekik egy darab papírt, mielőtt elkezdenék.
felsorolás 5
https://play.golang.org/p/BnVEHRCcdh
01 func waitForTask() {02 ch := make(chan string)0304 go func() {05 p := <-ch0607 // Employee performs work here.0809 // Employee is done and free to go.10 }()1112 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)1314 ch <- "paper"15 }
a 02. sorban az 5. listában egy nem pufferelt csatorna jön létre azzal az attribútummal, hogy string
adatokat küld a jellel. Ezután a 04-es vonalon egy alkalmazottat felvesznek, és azt mondják, hogy várjon a jelre a 05-ös vonalon, mielőtt elvégezné a munkáját. A 05-ös vonal a csatorna fogadása, ami az alkalmazott blokkolását okozza, miközben várja az elküldött papírdarabot. Miután a munkavállaló megkapta a papírt, a munkavállaló elvégzi a munkát, majd elkészül és szabadon távozhat.
Ön, mint vezető, egyidejűleg dolgozik az új alkalmazottjával. Tehát miután felvette a munkavállalót a 04-es vonalon, találja magát (a 12-es vonalon), amit meg kell tennie, hogy feloldja és jelezze a munkavállalót. Megjegyzés, nem volt ismert, hogy mennyi ideig tart, hogy előkészítse ezt a darab papírt kell küldeni.
végül készen áll arra, hogy jelezze a munkavállalót. A 14-es vonalon egy adatot tartalmazó jelet hajt végre, az adat az a papírdarab. Mivel nem pufferelt csatornát használnak, garanciát kap arra, hogy a munkavállaló megkapta a papírt, miután a küldési művelet befejeződött. A fogadás a küldés előtt történik.
technikailag csak annyit tudsz, hogy az alkalmazottnak megvan a papírja, mire a csatornaküldési művelet befejeződik. Mindkét csatorna művelet után az ütemező választhat, hogy végrehajtja-e a kívánt utasítást. A következő kódsor, amelyet Ön vagy a munkavállaló hajt végre, nem determinisztikus. Ez azt jelenti, hogy a nyomtatott nyilatkozatok használata megtévesztheti a dolgok sorrendjét.
2.forgatókönyv – várakozás az eredményre
ebben a következő forgatókönyvben a dolgok megfordulnak. Ezúttal azt szeretné, hogy az új alkalmazott azonnal elvégezzen egy feladatot, amikor felveszik őket, és meg kell várnia a munkájuk eredményét. Meg kell várni, mert szüksége van a papír tőlük, mielőtt továbbra is.
felsorolás 6
https://play.golang.org/p/VFAWHxIQTP
01 func waitForResult() {02 ch := make(chan string)0304 go func() {05 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)0607 ch <- "paper"0809 // Employee is done and free to go.10 }()1112 p := <-ch13 }
a 02. sorban a 6. listában egy nem pufferelt csatorna jön létre azzal az attribútummal, hogy string
adatokat küld a jellel. Ezután a 04-es vonalon egy alkalmazottat felvesznek, és azonnal munkába állítják. Miután felvette a munkavállalót a 04-es vonalon, a következő sorban találja magát 12 várja a papírjelentést.
miután a munkavállaló befejezte a munkát a 05-ös vonalon, elküldi az eredményt a 07-es vonalon egy csatornaküldés végrehajtásával. Mivel ez egy nem pufferelt csatorna, a fogadás a küldés előtt történik, és az alkalmazott garantálja, hogy megkapta az eredményt. Miután a munkavállaló megkapta ezt a garanciát, készen állnak és szabadon távozhatnak. Ebben a forgatókönyvben fogalma sincs arról, hogy mennyi időbe telik az alkalmazottnak a feladat befejezése.
költség/haszon
a nem pufferelt csatorna garantálja, hogy a küldött jel érkezett. Ez nagyszerű, de semmi sem ingyenes. A garancia költsége ismeretlen késleltetés. A várakozás a feladatra forgatókönyvben az alkalmazottnak fogalma sincs arról, hogy mennyi időbe telik, amíg elküldi ezt a papírt. Az eredmény várakozása forgatókönyvben fogalma sincs arról, hogy mennyi időbe telik az alkalmazottnak az eredmény elküldése.
mindkét esetben ezzel az ismeretlen késleltetéssel együtt kell élnünk, mert a garancia szükséges. A logika nem működik e garantált viselkedés nélkül.
jel adatokkal-nincs garancia-pufferelt csatornák >1
ha nem kell tudnia, hogy a küldött jel érkezett, akkor ez a két forgatókönyv jön létre: Fan Out and Drop.
a pufferelt csatorna jól meghatározott területtel rendelkezik, amely felhasználható az elküldött adatok tárolására. Tehát hogyan dönti el, hogy mennyi helyre van szüksége? Válaszoljon ezekre a kérdésekre:
- van egy jól meghatározott mennyiségű munka, amelyet be kell fejeznem?
- mennyi munka van?
- ha a munkavállaló nem tud lépést tartani, tudok dobni minden új munkát?
- mennyi kiemelkedő munka tesz engem kapacitássá?
- milyen kockázatot vállalok, ha a programom váratlanul véget ér?
- bármi, ami a pufferben vár, elvész.
ha ezeknek a kérdéseknek nincs értelme a modellezett viselkedésnek, akkor kódszag, hogy az 1-nél nagyobb pufferelt csatorna használata valószínűleg helytelen.
forgatókönyv 1 – Fan Out
a fan out minta lehetővé teszi, hogy dobja egy jól meghatározott számú alkalmazott a probléma, akik egyidejűleg dolgoznak. Mivel minden feladathoz egy alkalmazottja van, pontosan tudja, hány jelentést fog kapni. Meggyőződhet arról, hogy a dobozban megfelelő mennyiségű hely van-e az összes jelentés fogadásához. Ennek az az előnye, hogy alkalmazottainak nem kell megvárniuk a jelentés benyújtását. Mindazonáltal meg kell fordulniuk, ha a jelentést a dobozba helyezik, ha ugyanabban az időben vagy annak közelében érkeznek a dobozba.
Képzeld el, hogy újra vezető vagy, de ezúttal egy csapat alkalmazottat veszel fel. Van egy egyedi feladat, amelyet minden alkalmazottnak el kell végeznie. Mivel minden egyes alkalmazott befejezi feladatát, papíralapú jelentést kell benyújtania Önnek, amelyet az íróasztal dobozába kell helyezni.
felsorolás 7
https://play.golang.org/p/8HIt2sabs_
01 func fanOut() {02 emps := 2003 ch := make(chan string, emps)0405 for e := 0; e < emps; e++ {06 go func() {07 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)08 ch <- "paper"09 }()10 }1112 for emps > 0 {13 p := <-ch14 fmt.Println(p)15 emps--16 }17 }
a 03. sorban a 7. listában egy pufferelt csatorna jön létre azzal az attribútummal, hogy string
adatokat küld a jellel. Ezúttal a csatorna 20 pufferrel jön létre a 02.sorban deklarált emps
változónak köszönhetően.
a 05-től 10-ig terjedő sorok között 20 alkalmazottat vesznek fel, akik azonnal munkába állnak. Fogalmad sincs, mennyi ideig tart minden alkalmazott a 07-es vonalon. Ezután a 08. sorban az alkalmazottak elküldik a papírjelentést, de ezúttal a Küldés nem blokkolja a fogadás várakozását. Mivel minden alkalmazott számára van hely a dobozban, a csatornán történő küldés csak olyan alkalmazottakkal versenyez, akik esetleg ugyanabban az időben vagy annak közelében szeretnék elküldeni jelentésüket.
a 12-től 16-ig terjedő sorok közötti kód csak te vagy. Itt várja meg, hogy mind a 20 alkalmazott befejezze munkáját és elküldje jelentését. A 12-es vonalon hurokban vagy, a 13-as vonalon pedig blokkolva van egy csatorna fogadása, amely a jelentésekre vár. A jelentés beérkezése után a jelentés kinyomtatásra kerül a 14.sorban, és a helyi számláló változó csökken, jelezve, hogy egy alkalmazott elkészült.
2.forgatókönyv – csepp
a csepp minta lehetővé teszi, hogy dobja el a munkát, amikor a munkavállaló(k) a kapacitás. Ennek az az előnye, hogy továbbra is elfogadja az ügyfelek munkáját, és soha nem alkalmaz ellennyomást vagy késleltetést a munka elfogadásakor. A legfontosabb itt az, hogy tudjuk, mikor vagyunk valóban kapacitással, így nem vállaljuk, hogy mennyi munkát próbálunk elvégezni. Általában integrációs tesztelésre vagy mutatókra van szükség a szám azonosításához.
Képzeld el, hogy újra vezető vagy, és egyetlen alkalmazottat veszel fel a munka elvégzésére. Van egy egyedi feladat, amelyet a munkavállalónak végre kell hajtania. Amint a munkavállaló befejezi a feladatát, nem érdekli, hogy készen vannak-e. Csak az a fontos, hogy új munkát helyezhet-e a dobozba. Ha nem tudja végrehajtani a küldést, akkor tudja, hogy a doboz tele van, és az alkalmazott kapacitással rendelkezik. Ezen a ponton az új munkát el kell dobni, hogy a dolgok tovább mozoghassanak.
felsorolás 8
https://play.golang.org/p/PhFUN5itiv
01 func selectDrop() {02 const cap = 503 ch := make(chan string, cap)0405 go func() {06 for p := range ch {07 fmt.Println("employee : received :", p)08 }09 }()1011 const work = 2012 for w := 0; w < work; w++ {13 select {14 case ch <- "paper":15 fmt.Println("manager : send ack")16 default:17 fmt.Println("manager : drop")18 }19 }2021 close(ch)22 }
a 03. sorban a 8. listában egy pufferelt csatorna jön létre azzal az attribútummal, hogy string
adatokat küld a jellel. Ezúttal a csatorna 5 pufferrel jön létre a 02-es vonalon deklarált cap
állandónak köszönhetően.
a 05-09 sorok között egyetlen alkalmazottat vesznek fel a munka kezelésére. A for range
a csatorna vételére szolgál. Minden alkalommal, amikor egy darab papírt kap, azt a 07-es vonalon dolgozzák fel.
a 11-től 19-ig terjedő sorok között 20 darab papírt próbál küldeni az alkalmazottjának. Ezúttal egy select
utasítást használunk a Küldés végrehajtására az első case
– en belül a 14.sorban. Mivel a default
záradékot a select
a 16.sorban használják, ha a Küldés blokkolni fog, mert nincs több hely a pufferben, a küldést a 17. sor végrehajtásával elhagyják.
végül a 21.sorban a beépített funkció close
a csatorna ellen kerül meghívásra. Ez adat nélkül jelzi a munkavállalónak, hogy elvégezték és szabadon távozhatnak, miután befejezték a kijelölt munkát..
költség/haszon
az 1-nél nagyobb pufferelt csatorna nem garantálja, hogy az elküldött jelet valaha is fogadják. Van egy előnye, hogy elhagyja ezt a garanciát, ami a két goroutine közötti kommunikáció csökkent vagy nincs késleltetése. A Fan Out forgatókönyvben minden alkalmazott számára van egy pufferterület, amely jelentést küld. A leesési forgatókönyvben a puffert a kapacitás méri, és ha a kapacitás eléri a munkát, akkor a dolgok tovább mozoghatnak.
mindkét lehetőségnél a garancia hiányával együtt kell élnünk, mert a késleltetés csökkentése fontosabb. A nullától a minimális késleltetésig terjedő követelmény nem jelent problémát a rendszer általános logikájában.
adatjel – késleltetett garancia – pufferelt 1.csatorna
ha új jel küldése előtt tudni kell, hogy az előző küldött jel érkezett-e, akkor a feladatok várakozása forgatókönyv lép életbe.
1.forgatókönyv – feladatok várása
ebben a forgatókönyvben van egy új alkalmazott, de ők nem csak egy feladatot fognak elvégezni. Fogsz etetni őket sok feladat, egyik a másik után. Azonban minden egyes feladatot be kell fejezniük, mielőtt újat kezdhetnek. Mivel egyszerre csak egy feladaton tudnak dolgozni, késleltetési problémák merülhetnek fel a munka átadása között. Ha a késleltetés csökkenthető anélkül, hogy elveszítené a garanciát arra, hogy a munkavállaló a következő feladaton dolgozik, segíthet.
itt van az 1 pufferelt csatornájának előnye. Ha minden a várt ütemben halad közted és a munkavállaló között, egyiküknek sem kell várnia a másikra. Minden alkalommal, amikor egy darab papírt küld, a puffer üres. Minden alkalommal, amikor alkalmazottja több munkához jut, a puffer megtelt. Ez a munkafolyamat tökéletes szimmetriája.
a legjobb rész ez. Ha bármikor megpróbál küldeni egy darab papírt, és nem tudja, mert a puffer tele van, akkor tudja, hogy az alkalmazottnak problémája van, és abbahagyja. Itt jön be a késedelmes garancia. Ha a puffer üres, és végrehajtja a küldést, akkor garantálhatja, hogy alkalmazottja elvégezte az utoljára elküldött munkát. Ha végrehajtja a küldést, de nem tudja, akkor garantálja, hogy nem.
lista 9
https://play.golang.org/p/4pcuKCcAK3
01 func waitForTasks() {02 ch := make(chan string, 1)0304 go func() {05 for p := range ch {06 fmt.Println("employee : working :", p)07 }08 }()0910 const work = 1011 for w := 0; w < work; w++ {12 ch <- "paper"13 }1415 close(ch)16 }
a 02. sorban a 9. listában egy 1 méretű pufferelt csatorna jön létre azzal az attribútummal, hogy string
adatokat küld a jellel. Sorok között 04 keresztül 08 egyetlen alkalmazottat vesznek fel a munka kezelésére. A for range
a csatorna vételére szolgál. Minden alkalommal, amikor egy darab papírt kap, azt a 06-os vonalon dolgozzák fel.
a 10-től 13-ig terjedő sorok között elkezdi elküldeni a feladatokat az alkalmazottnak. Ha alkalmazottja olyan gyorsan tud futni, amennyit csak tud küldeni, akkor a kettő közötti késleltetés csökken. De minden küldéssel, amelyet sikeresen teljesít, garantálja, hogy az utolsó beküldött munkán dolgozik.
végül a 15.sorban a beépített funkció close
a csatorna ellen kerül meghívásra. Ez adat nélkül jelzi a munkavállalónak, hogy készen állnak és szabadon távozhatnak. Az utolsó beküldött munka azonban a for range
befejezése előtt beérkezik (kipirul).
Signal without Data – Context
ebben az utolsó forgatókönyvben láthatja, hogyan törölheti a futó goroutint a Context
érték használatával a context
csomagból. Mindez egy nem pufferelt csatorna kihasználásával működik, amely zárva van, hogy adat nélküli jelet hajtson végre.
utoljára te vagy a menedzser, és egyetlen alkalmazottat veszel fel a munka elvégzésére. Ezúttal nem hajlandó várni egy ismeretlen ideig, amíg a munkavállaló befejezi. Ön diszkrét határidőn belül van, és ha a munkavállaló nem fejezi be időben, akkor nem hajlandó várni.
felsorolás 10
https://play.golang.org/p/6GQbN5Z7vC
01 func withTimeout() {02 duration := 50 * time.Millisecond0304 ctx, cancel := context.WithTimeout(context.Background(), duration)05 defer cancel()0607 ch := make(chan string, 1)0809 go func() {10 time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)11 ch <- "paper"12 }()1314 select {15 case p := <-ch:16 fmt.Println("work complete", p)1718 case <-ctx.Done():19 fmt.Println("moving on")20 }21 }
a 02. sorban a 10. listában egy időtartam értéket deklarálnak, amely azt jelzi, hogy a munkavállalónak mennyi ideig kell befejeznie a feladatot. Ez az érték a 04-es sorban context.Context
értéket hoz létre 50 milliszekundumos időkorláttal. A WithTimeout
függvény a context
csomagból egy Context
értéket és egy törlési függvényt ad vissza.
a context
csomag létrehoz egy goroutint, amely bezárja a Context
értékkel társított nem pufferelt csatornát, amint az időtartam teljesül. Ön felelős a cancel
függvény hívásáért, függetlenül attól, hogy a dolgok hogyan alakulnak. Ez megtisztítja a Context
számára létrehozott dolgokat. Rendben van, ha a cancel
függvényt többször is meghívják.
a 05-ös vonalon az cancel
függvény végrehajtása elhalasztásra kerül, ha ez a funkció véget ér. A 07-es vonalon létrejön az 1-es pufferelt csatorna, amelyet az alkalmazott arra fog használni, hogy elküldje Önnek munkájuk eredményét. Ezután a 09-12-es vonalakon a munkavállalót felveszik és azonnal munkába állítják. Fogalmad sincs, mennyi időbe telik az alkalmazottnak befejezni.
a 14-től 20-ig tartó sorok között a select
utasítást használja két csatornán történő fogadáshoz. A fogadás a 15. sorban várja meg, amíg a munkavállaló elküldi Önnek az eredményét. A fogadás a 18. sorban várja meg, hogy a context
csomag jelzi-e, hogy az 50 milliszekundum lejárt. Bármelyik jelet is kapja meg először, az lesz a feldolgozott.
ennek az algoritmusnak fontos szempontja az 1 pufferelt csatornájának használata. Ha a munkavállaló nem fejezi be időben, akkor tovább lép, anélkül, hogy értesítést adna a munkavállalónak. Az alkalmazottak szemszögéből mindig elküldik a jelentést a 11. vonalon, és vakok, ha ott vagy, vagy nem kapják meg. Ha nem pufferelt csatornát használ, az alkalmazott örökre blokkolja a jelentés elküldését, ha továbblép. Ez gorutin szivárgást okozna. Tehát egy 1-es pufferelt csatornát használnak ennek megakadályozására.
következtetés
a garanciák, a csatornaállapot és a Küldés körüli jelzés attribútumait fontos tudni és megérteni a csatornák (vagy párhuzamosság) használatakor. Segítenek Önnek abban, hogy végrehajtsa a legjobb viselkedést, amire szüksége van az Ön által írt egyidejű programokhoz és algoritmusokhoz. Segítenek megtalálni a hibákat és kiszimatolni a potenciálisan rossz kódot.
ebben a bejegyzésben megosztottam néhány mintaprogramot, amelyek megmutatják, hogyan működnek a jelzés attribútumai különböző forgatókönyvekben. Minden szabály alól vannak kivételek, de ezek a minták jó alapot jelentenek a kezdéshez.
tekintse át ezeket a körvonalakat, hogy összefoglalja, mikor és hogyan kell hatékonyan gondolkodni és használni a csatornákat:
nyelvi mechanika
- csatornákat használ a gorutinok hangszerelésére és koordinálására.
- Fókuszban a jelátviteli attribútumok, nem pedig az adatok megosztása.
- jelzés adatokkal vagy adatok nélkül.
- megkérdőjelezik a megosztott állapothoz való hozzáférés szinkronizálásának használatát.
- vannak olyan esetek, amikor a csatornák egyszerűbbek lehetnek ehhez, de kezdetben kérdés.
- nem pufferelt csatornák:
- a fogadás a küldés előtt történik.
- előny: 100% – os garancia a jel fogadására.
- költség: Ismeretlen késleltetés, amikor a jel érkezik.
- pufferelt csatornák:
- a Küldés a fogadás előtt történik.
- előny: csökkentse a jelzés közötti blokkolási késleltetést.
- költség: nincs garancia, ha a jel érkezett.
- minél nagyobb a puffer, annál kevesebb a garancia.
- puffer 1 adhat egy késleltetett küldési garancia.
- csatornák bezárása:
- a Bezárás A fogadás előtt történik (például pufferelt).
- jelzés adat nélkül.
- Tökéletes a törlések és határidők jelzésére.
- nil csatornák:
- Küldés és fogadás blokk.
- kapcsolja ki a jelzést
- tökéletes sebességkorlátozáshoz vagy rövid távú leállásokhoz.
tervezési filozófia
- ha egy csatornán egy adott Küldés blokkolhatja a küldő goroutint:
- nem használhat 1-nél nagyobb pufferelt csatornát.
- az 1-nél nagyobb Puffereknek okkal/mérésekkel kell rendelkezniük.
- tudnia kell, mi történik, ha a küldő goroutine blokkol.
- nem használhat 1-nél nagyobb pufferelt csatornát.
- ha egy adott Küldés egy csatornán nem okozza a küldő goroutine blokkolását:
- megvan a pontos pufferek száma minden küldéshez.
- ventilátor ki Minta
- megvan a puffer mért maximális kapacitás.
- csepp minta
- megvan a pontos pufferek száma minden küldéshez.
- a pufferekkel kevesebb több.
- ne gondoljon a teljesítményre, amikor pufferekre gondol.
- a pufferek segíthetnek csökkenteni a jelzés közötti blokkolási késleltetést.
- a blokkolási késleltetés nullára csökkentése nem feltétlenül jelent jobb átviteli sebességet.
- ha egy puffer elég jó teljesítményt nyújt, akkor tartsa meg.
- kérdés pufferek, amelyek nagyobbak, mint egy, és intézkedés a méret.
- keresse meg a lehető legkisebb puffert, amely elég jó teljesítményt nyújt.