# Concorrência Diz-se que Go é a linguagem C do século XXI. Eu acho que existem duas razões: primeiro, o Go é uma linguagem simples; segundo, a simultaneidade é um tema importante no mundo atual, e o Go suporta esse recurso no nível da linguagem. ## goroutine goroutines e simultaneidade são incorporados ao design central do Go. Eles são semelhantes aos tópicos, mas funcionam de maneira diferente. Mais de uma dúzia de goroutines talvez tenham apenas 5 ou 6 threads subjacentes. Go também lhe dá suporte total para compartilhar memória em seus goroutines. Uma goroutine geralmente usa 4 ~ 5 KB de memória de pilha. Portanto, não é difícil executar milhares de goroutines em um único computador. Uma goroutine é mais leve, mais eficiente e mais conveniente que os threads do sistema. Os goroutines são executados no gerenciador de encadeamentos em tempo de execução no Go. Usamos a palavra-chave `go` para criar uma nova goroutine, que é uma função no nível subjacente (*** main () é uma goroutine ***). go hello(a, b, c) Vamos ao exemplo: package main import ( "fmt" "runtime" ) func say(s string) { for i := 0; i < 5; i++ { runtime.Gosched() fmt.Println(s) } } func main() { go say("world") // create a new goroutine say("hello") // current goroutine } Retorno hello world hello world hello world hello world hello Vemos que é muito fácil usar a simultaneidade no Go usando a palavra-chave `go`. No exemplo acima, essas duas goroutines compartilham alguma memória, mas seria melhor seguir a receita de design: Não use dados compartilhados para se comunicar, use a comunicação para compartilhar dados. runtime.Gosched () significa deixar a CPU executar outras goroutines e voltar em algum momento. O agendador usa apenas um thread para executar todos os goroutines, o que significa que ele apenas implementa a simultaneidade. Se você deseja usar mais núcleos de CPU para aproveitar o processamento paralelo, é necessário chamar runtime.GOMAXPROCS (n) para definir o número de núcleos que deseja usar. Se `n <1`, nada muda. Esta função pode ser removida no futuro, veja mais detalhes sobre o processamento paralelo e simultaneidade neste [artigo(em inglês)](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide). ## Canais(Channels) Os goroutines são executados no mesmo espaço de endereço de memória, portanto, você precisa manter a sincronização quando quiser acessar a memória compartilhada. Como você se comunica entre diferentes goroutines? Go usa um mecanismo de comunicação muito bom chamado `channel`. `channel` é como um pipeline bidirecional em shells Unix: use` channel` para enviar ou receber dados. O único tipo de dado que pode ser usado em canais é o tipo `channel` e a palavra-chave` chan`. Esteja ciente de que você tem que usar `make` para criar um novo` channel`. ci := make(chan int) cs := make(chan string) cf := make(chan interface{}) canais usa, o operador `<-` para enviar ou receber dados. ch <- v // envia v para o canal ch. v := <-ch // recebe dados de ch, e os assina em v Exemplos: package main import "fmt" func sum(a []int, c chan int) { total := 0 for _, v := range a { total += v } c <- total // envia o total para c } func main() { a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // recebe de c fmt.Println(x, y, x + y) } Enviando e recebendo dados em blocos de canais por padrão, é muito mais fácil usar goroutines síncronas. O que quero dizer com block é que uma goroutine não continuará ao receber dados de um canal vazio, ou seja, (`value: = <-ch`), até que outras goroutines enviem dados para este canal. Por outro lado, a goroutine não continuará até que os dados enviados a um canal, ou seja (`ch <-5`), sejam recebidos. ## Canais em Buffer Eu introduzi canais não-bufferizados acima. Go também tem canais em buffer que podem armazenar mais de um único elemento. Por exemplo, `ch: = make (chan bool, 4)`, aqui criamos um canal que pode armazenar 4 elementos booleanos. Assim, neste canal, podemos enviar 4 elementos sem bloqueio, mas a goroutine será bloqueada quando você tentar enviar um quinto elemento e nenhuma goroutine o receber. ch := make(chan type, n) n == 0 ! non-buffer(block) n > 0 ! buffer(non-block until n elements in the channel) Você pode tentar o seguinte código no seu computador e alterar alguns valores. package main import "fmt" func main() { c := make(chan int, 2) // altera de 2 para 1 e retornará um erro, mas 3 funciona c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } ## Alcance e fechamento(Range and Close) Podemos usar o range para operar em canais buffer como em slice e map. package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 1, 1 for i := 0; i < n; i++ { c <- x x, y = y, x + y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } `for i := range c` não parará de ler dados do canal até que o canal seja fechado. Usamos a palavra-chave `close` para fechar o canal no exemplo acima. É impossível enviar ou receber dados em um canal fechado; você pode usar `v, ok: = <-ch` para testar se um canal está fechado. Se `ok` retornar falso, significa que não há dados nesse canal e foi fechado. Lembre-se sempre de fechar os canais nos produtores e não nos consumidores, ou é muito fácil entrar em status de pânico. Outra coisa que você precisa lembrar é que os canais não são como arquivos. Você não precisa fechá-los com frequência, a menos que tenha certeza de que o canal é completamente inútil ou deseja sair de loops de intervalo. ## Select Nos exemplos acima, usamos apenas um canal, mas como podemos lidar com mais de um canal? Go tem uma palavra-chave chamada `select` para ouvir muitos canais. `select` está bloqueando por padrão e continua a executar somente quando um dos canais tem dados para enviar ou receber. Se vários canais estiverem prontos para usar ao mesmo tempo, selecione a opção para executar aleatoriamente. package main import "fmt" func fibonacci(c, quit chan int) { x, y := 1, 1 for { select { case c <- x: x, y = y, x + y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) } `select` tem um caso` default`, assim como `switch`. Quando todos os canais não estão prontos para uso, ele executa o caso padrão (ele não aguarda mais o canal). select { case i := <-c: // use i default: // Executa aqui quando C estiver bloqueado } ## Timeout Às vezes uma goroutine fica bloqueada. Como podemos evitar isso para evitar que todo o programa bloqueie? É simples, podemos definir um tempo limite no select. func main() { c := make(chan int) o := make(chan bool) go func() { for { select { case v := <- c: println(v) case <- time.After(5 * time.Second): println("timeout") o <- true break } } }() <- o } ## Runtime goroutine O pacote `runtime` tem algumas funções para lidar com goroutines. - `runtime.Goexit ()` Sai da gorout atual, mas as funções adiadas serão executadas como de costume. - `runtime.Gosched ()` Permite que o planejador execute outras goroutines e volte em algum momento. - `runtime.NumCPU () int` Retorna o número de núcleos da CPU - `runtime.NumGoroutine () int` Retorna o número de goroutines - `runtime.GOMAXPROCS (n int) int` Define quantos núcleos de CPU você deseja usar ## Links - [Prefácio](preface.md) - Seção anterior: [interfaces](02.6.md) - Próxima seção: [Summary](02.8.md)