în acest tutorial, vom analiza modul în care puteți utiliza canalele din aplicațiile dvs. bazate pe Go.
canalele sunt conducte care fac legătura între goroutines
în aplicațiile bazate pe Go care permit comunicarea și, ulterior, transmiterea valorilor către și din variabile.
acestea sunt incredibil de la îndemână și vă pot ajuta să ambarcațiunile de incredibil de înaltă performanță,aplicații extrem de concurente în Du-te cu tam-tam minim în comparație cu otherprogramming limbi. Acest lucru nu a fost în niciun caz o întâmplare, atunci când au proiectat limba, dezvoltatorii de bază au decis că doresc ca concurența în limba lor să fie un cetățean de primă clasă și să facă cât mai simplu să lucreze cu caposibil, fără a merge prea departe și a nu permite dezvoltatorilor libertatea în care trebuie să lucreze.
abilitatea de a crea sisteme concurente atât de ușor este ceva care m-a atras în primul rând la limbaj, și trebuie să spun, a fost o lumină absolută până acum.
notă – mi-ar recomanda având o privire la meu alte tutorial ongoroutines dacă doriți să learnmore despre goroutines.
obiective
până la sfârșitul acestui tutorial, veți:
- au o înțelegere solidă cu privire la teoria din spatele canale
- să fie capabil de a crea aplicații simple concurente Go care utilizează canale
premise
pentru a finaliza acest tutorial, va trebui să fi îndeplinit următoareleprerequisites:
- veți avea nevoie de GO instalat pe mașina dvs.
Tutorial Video
dacă doriți, acest tutorial este disponibil în format video.
teoria
ideea canalelor nu este nimic nou, deoarece, la fel ca multe dintre caracteristicile concurente ale lui Go, aceste concepte au fost aduse din like-urile proceselor secvențiale de comunicare ale lui Hoare (1978), CSP pe scurt, și chiar din like-urile comenzilor păzite ale lui Dijkstra (1975).
dezvoltatorii de Go, cu toate acestea, au făcut misiunea lor de a prezenta aceste concepte într-un mod simplu posibil pentru a permite programatorilor să creezeaplicații mai bune, mai corecte, extrem de concurente.
un exemplu simplu
să începem prin a vedea cum putem construi un exemplu foarte simplu de howthis funcționează în Go. Vom crea mai întâi o funcție care dispare și calculează valoarea anarbitrară, aleatorie și o transmite înapoi la o variabilă de canal numităvalues
:
să disecăm ce s-a întâmplat aici. În funcția noastră main()
, am sunatvalues := make(chan int)
, acest apel a creat în mod eficient noul nostru canal, astfel încât să-l putem folosi ulterior în CalculateValue
goroutine.
Notă – Am folosit
make
la instanțierea canalului nostruvalues
ca, cum ar fi Hărți și felii, canalele trebuie create înainte de utilizare.
după ce am creat canalul, am sunat apoi defer close(values)
care a împiedicat închiderea canalului nostru până la sfârșitul execuției funcției noastre main()
. Aceasta este de obicei considerată cea mai bună practică pentru a ne asigura că ne ordonămdupă noi înșine.
după apelul nostru la defer
, continuăm să lansăm singurul nostru goroutine: CalculateValue(values)
trecând în noul nostru canal values
ca parametru. În cadrul funcției noastre CalculateValue
, calculăm o singură valoare aleatorie între 1-10, tipărim acest lucru și apoi trimitem această valoare canalului nostru values
apelând values <- value
.
sărind înapoi în funcția noastră main()
, apoi apelăm value := <-values
whichreceives a value from our values
canal.
notă – observați cum atunci când executăm acest program, nu se termină imediat. Acest lucru se datorează faptului că actul de a trimite și primi de la un canalsunt blocate. Funcția noastră
main()
se blochează până când primește o valoare de la canalul nostru.
la executarea acestui cod, ar trebui să vedeți ieșirea arata ceva de genul asta:
$ go run main.goGo Channel TutorialCalculated Random Value: {} 77
rezumat:
myChannel := make(chan int)
– creează myChannel care este un canal de tipint
channel <- value
– trimite o valoare unui canal
value := <- channel
– primește o valoare de la un canal
deci, instanțierea și utilizarea canalelor în programele Go arată corectstraightforward până acum, dar ce se întâmplă în scenarii mai complexe?
canale Unbuffered
folosind un tradițional channel
în goroutines dvs. poate duce uneori la probleme cu un comportament care nu poate fi destul de așteaptă. Cu canalele tradiționale unbuffered
, ori de câte ori un goroutine trimite o valoare acestui canal,acel goroutine va bloca ulterior până când valoarea este primită de la canal.
să vedem acest lucru într-un exemplu real. Dacă ne uităm la codul de mai jos, este foartesimilar cu codul pe care l-am avut anterior. Cu toate acestea, ne-am extins funcțiaCalculateValue()
pentru a efectua o fmt.Println
după ce a trimis valoarea calculată la întâmplare pe canal.
în funcția noastră main()
, am adăugat un al doilea apel lago CalculateValue(valueChannel)
deci ar trebui să ne așteptăm la 2 valori trimise la thischannel într-o succesiune foarte rapidă.
cu toate acestea, atunci când executați acest lucru, ar trebui să vedeți că numai prima noastră declarație goroutines ‘ finalprint este de fapt executat:
go run main.goGo Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 71Only Executes after another goroutine performs a receive on the channel
motivul pentru aceasta este apelul nostru la c <- value
a blocat în secondgoroutine nostru și, ulterior, main()
funcția încheie este executarea înainte de al doilea nostru goroutine
devine o șansă de a finaliza propria execuție.
canale tamponate
modul de a ocoli acest comportament de blocare este de a folosi ceva numit canal abuffered. Aceste canale tamponate sunt în esență cozi de o anumită dimensiunecare poate fi utilizat pentru comunicarea încrucișată. Pentru a crea un canal abuffered, spre deosebire de un canal unbuffered, furnizăm un capacityargument comenzii noastre make
:
bufferedChannel := make(chan int, 3)
schimbând acest lucru într-un canal tamponat, operația noastră de trimitere, c <- value
blochează numai în goroutinele noastre în cazul în care canalul este plin.
să modificăm programul nostru existent pentru a utiliza un canal tamponat și să aruncăm o privire la ieșire. Observați că am adăugat un apel la time.Sleep()
în partea de jos a funcției noastremain()
pentru a bloca Alene funcția noastră main()
suficient pentru a permite goroutines noastre pentru a finaliza executarea.
acum, când executăm acest lucru, ar trebui să vedem că a doua goroutină continuă într-adevăr executarea sa, indiferent de faptul că o a doua primire nu a fost apelată în funcția noastră main()
. Datorită time.Sleep()
, putem vedea în mod clar diferența dintre canalele nebuffered și natura lor de blocare și canalele noastre înfundate și natura lor non-blocantă (atunci când nu este completă).
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
concluzie
Deci, în acest tutorial destul de lung, am reușit să aflăm despre diferitele tipuri distincte de canale din Go. Am descoperit diferențele dintre canalele bothbuffered și unbuffered și cum le-am putea folosi în avantajul nostru în programele noastre concurente go.
dacă v-a plăcut acest tutorial, atunci vă rugăm să nu ezitați să mă anunțați în secțiunea comentarii de mai jos. Dacă aveți sugestii cu privire la ceea ce aș putea face mai bineapoi mi-ar plăcea să le aud în secțiunea de comentarii de mai jos!
lecturi suplimentare
dacă v-a plăcut acest articol și doriți să aflați mai multe despre lucrul cu Concurențaîn Go, atunci vă recomand să consultați celelalte articole despre concurență:
- Go Mutex Tutorial
- Go Goroutines Tutorial
- Go sync.WaitGroup Tutorial