Gorrotinas e Canais

go f() // cria uma nova gorrotina que chama f()

Canais

Os canais sãos os meios de comunicação entre as gorrotinas Cada canal é um comunicador de um tipo particular

ch := make(chan int) // ch é do tipo chan int

Um canal é uma referência a estrutura criada por make

Para fechar um canal

close(ch)

Pipelines

Canais unidirecionais

Canais sem buffer

Uma gorrotina sem buffer bloquei a gorrotina que envia até o receptor executar a recepção e também bloquei a recptora até receber um envio.

func main(){
	conn, err := net.Dial("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	done := make(chan struct{}) // usamos uma struct vazia para enfatizar que o canal não transporta qualquer informação
	go func(){
		io.Copy(os.Stdout, conn)
		log.Println("done")
		done <- struct{}{}
	}
	mustCopy(conn, os.Stdin)
	conn.Close()
	<-done // espera a gorrotina em background terminar (sincronização)
}

Pipelines

Gorrotinas podem ser conectadas de formar que a saida de uma seja a entrada de outra, isso se chama pipeline

func main(){
	naturals := make(chan int)
	squares := make(chan int)
	go func(){
		for x:=0; ;x++{
			naturals <- x
		}
	}()
	go func(){
		for {
			x := <- naturals
			squares <- x * x
		}
	}
	for {
		fmt.Println(<-squares)
	}
}

Tipos de canais

chan<- int - canal somente para envio <-chan int - canal somente para recebimento

Canais com buffer

Canais com buffers, garantem o envio de mensagens para uma fila com tamanho prefixado, desta forma caso a fila não esteja completa o canal de envio não será bloqueado

Looping em paralelo

for _, f := range filenames {
	go func(){
		thumbnail.Imagefile(f) // *** INCORRETO ***
	}()
}

for _, f := range filenames {
	go func(f string){
		thumbnail.Imagefile(f) // *** INCORRETO ***
	}(f)
}

No exemplo acima acessamos a váriavel f internamente na gorrotina, porém o uso neste caso é incorreto pois a variavel f é atualizada a cada iteração do loop


for _, f := range filenames {
	go func(f string){
		_, err := thumbnail.Imagefile(f) // *** INCORRETO ***
		errors <- err
	}(f)
}

for range filenames {
	if err := <- errors; err != nil {
		return err // incorreto vazamento de gorrotina
	}
}

Impondo limites em concorrencia

var tokens = make(chan struct{}, 20)

func crawl(url string)[] string {
	fmt.Println(url)
	token <- struct{}{} // adquire um token
	list, err := links.Extract(url)
	<-tokens // libera o token
}

O código acima implementa uma técnica de concorrencia chamada semaforo contador (counting semaphore), isso garante que no máximo 20 requisições sejam executadas em paralelo

📚 Referências

https://github.com/golang/go/wiki