MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Canale

Canalele (channels) reprezintă instrumente de comunicare între gorutine. Pentru a defini un canal se folosește cuvântul cheie chan:

chan tip_element

După cuvântul chan, se specifică tipul de date care va fi transmis prin canal. De exemplu:

var intCh chan int

Aici, variabila intCh reprezintă un canal care transmite date de tipul int.

Pentru a trimite date într-un canal sau, dimpotrivă, pentru a le primi din canal, se folosește operația <- (săgeata direcționată spre stânga). De exemplu, pentru a trimite date într-un canal:

intCh <- 5

În acest caz, numărul 5 este trimis în canal. Pentru a primi date din canal într-o variabilă:

val := <- intCh

Dacă anterior în canal a fost trimis numărul 5, atunci la executarea operației <- intCh, vom primi acest număr în variabila val.

De obicei, trimitătorul de date este o gorutină, iar receptorul este o altă gorutină.

La definirea simplă a unei variabile canal, aceasta va avea valoarea nil, adică, practic, canalul nu este inițializat. Pentru inițializare se folosește funcția make(). În funcție de definirea capacității canalului, acesta poate fi un canal bufferizat sau unul nebufferizat.

Canale nebufferizate

Pentru a crea un canal nebufferizat, se apelează funcția make() fără a specifica capacitatea canalului:

var intCh chan int = make(chan int) // canal pentru date de tipul int
strCh := make(chan string)  // canal pentru date de tipul string

Dacă canalul este gol, gorutina receptor va fi blocată până când canalul va conține date. Când gorutina trimitătoare trimite date, gorutina receptor primește aceste date și își reia execuția.

Gorutina trimitătoare poate trimite date doar într-un canal gol. Gorutina trimitătoare va fi blocată până când datele vor fi preluate din canal. De exemplu:

package main
import "fmt"

func main() {
     
    intCh := make(chan int) 
     
    go func(){
            fmt.Println("Gorutină începe")
            intCh <- 5 // blocare până când datele vor fi preluate de funcția main
    }()
    fmt.Println(<-intCh) // primirea datelor din canal
    fmt.Println("The End")
}

Prin canalul nebufferizat intCh, gorutina definită prin funcția anonimă trimite numărul 5:

intCh <- 5

Iar funcția main primește acest număr:

fmt.Println(<-intCh)

Fluxul de execuție al programului este următorul:

  • Funcția main este lansată. Aceasta creează canalul intCh și pornește o gorutină sub formă de funcție anonimă
  • Funcția main continuă să ruleze și este blocată pe linia fmt.Println(<-intCh) până când datele sunt primite
  • În paralel, gorutina anonimă se execută. La finalul execuției sale, aceasta trimite date prin canal: intCh <- 5. Gorutina este blocată până când funcția main primește datele
  • Funcția main primește datele trimise, se deblochează și continuă execuția

În acest caz, gorutina este definită sub formă de funcție anonimă și, prin urmare, are acces la mediul său, inclusiv la variabila intCh. Dacă lucrăm cu funcții obișnuite, atunci obiectele canalelor trebuie transmise ca parametri:

package main
import "fmt"

func main() {
     
    intCh := make(chan int) 
     
    go factorial(5, intCh)  // apelul gorutinei
    fmt.Println(<-intCh) // primirea datelor din canal
    fmt.Println("The End")
}

func factorial(n int, ch chan int){
     
    result := 1
    for i := 1; i <= n; i++{
        result *= i
    }
    fmt.Println(n, "-", result)
     
    ch <- result     // trimiterea datelor în canal
}

Observați cum este definit parametrul care reprezintă canalul de date de tipul int: ch chan int. Ieșirea în consolă a acestui program va fi:

5 - 120
120
The End