O semáforo no Go é como se uma flag, que vai ser um channel que vai se comunicar com as routines, quando o semáforo apontar que deu tudo certo a ideia é q parem os canais.
func main() { channel := make(chan int) ok := make(chan bool) go func() { for i := 0; i < 10; i++ { channel <- i } ok <- true }() go func() { for i := 0; i < 10; i++ { channel <- i } ok <- true }() go func() { <-ok <-ok close(channel) }() for number := range channel { fmt.Println(number) } }
Nesse exemplo a variável ok
serve como o semáforo, perceba que na terceira função eu espero que a variável ok
tenha sido esvaziada duas vezes para só então poder fechar o channel
Pipeline pattern
Até agora todas as vezes que usei o channel tinham sido de forma isolada, com a pipeline fazemos funções que retornam um channel e outras funções que recebem esse channel retornado e continuam o processamento. Aprendi a trabalhar com pipelines através da seguinte abordagem.
primeiro criei uma função generate que recebe numeros inteiros e retorna um channel de inteiros
func generate(numbers ...int) chan int { channel := make(chan int) go func() { for _, number := range numbers { channel <- number } }() return channel }
E depois uma segunda função para dividir que recebe um channel de inteiros e retorna outro channel de inteiros
func divide(input chan int) chan int { channel := make(chan int) go func() { for number := range input { channel <- number / 2 } close(channel) }() return channel }
porem no caso dessa função enquanto eu vou atribuindo o valor para o channel eu já atribuo ele dividido por 2.
Minha função main ficou da seguinte forma
func main() { numbers := generate(2, 4, 6) result := divide(numbers) fmt.Println(<-result) fmt.Println(<-result) fmt.Println(<-result) }
Onde em ordem os prints exibem 1
2
3
. Mas o que tá acontecendo de fato é. A função generate recebe o numero 2
, atribui ele ao seu channel, e tenta atribuir o valor 4 em seguida, não consegue pois o channel ainda não foi esvaziado, nesse momento a função divide inicia e recebe o valor do channel da primeira função e atribui ao seu channel, ele também vai tentar atribuir o próximo numero e não vai conseguir, então ele chama o println para poder esvaziar o seu channel e isso roda 3 vezes até os 3 números terem sido processados
Fun in
Imagine que temos 3 processos rodando em paralelo cada um com o seu próprio canal, agora temos uma segunda função que recebe os canais dos 3 processos e retorna um novo canal mandando os resultados dos 3 processos
O fun in é um afunilamento que faz com que não precisemos olhar para os 3 channels e sim para apenas 1 e termos o resultado dos 3
func main() { x := funnel(generateMsg("Hello"), generateMsg("World")) for i := 0; i < 10; i++ { fmt.Println(<-x) } } func generateMsg(s string) <-chan string { channel := make(chan string) go func() { for i := 0; ; i++ { channel <- fmt.Sprintf("String %s - Value: %d", s, i) time.Sleep(time.Duration(rand.Intn(255)) * time.Millisecond) } }() return channel } func funnel(channel1, channel2 <-chan string) <-chan string { channel := make(chan string) go func() { for { channel <- <-channel1 } }() go func() { for { channel <- <-channel2 } }() return channel }
ps: o operador <- <-
me deixou um pouco confuso no inicio porem ele significa que estamos pegando o valor do channel1 e passando para a variavel channel
o output dessa função vai ser
❯ go run main.go String World - Value: 0 String Hello - Value: 0 String World - Value: 1 String Hello - Value: 1 String World - Value: 2 String World - Value: 3 String Hello - Value: 2 String Hello - Value: 3 String World - Value: 4 String World - Value: 5
então somente olhando para a variável x
dá para ter o resultado dos dois channels que estão rodando em paralelo
Fan out
Basicamente o Fan out é o processo oposto do fun in, onde vamos ter apenas 1 canal mas queremos distribuir ele pro diversos canais
para fazer o fan out podemos reutilizar o código do algoritmo de divide basta que pegarmos o valor inicial e passarmos para a função divide mais de uma vez, por exemplo:
func main() { c := generate(5, 10) d1 := divide(c) d2 := divide(c) fmt.Println(<-d1) fmt.Println(<-d2) } func generate(numbers ...int) chan int { channel := make(chan int) go func() { for _, n := range numbers { channel <- n } close(channel) }() return channel } func divide(input chan int) chan int { channel := make(chan int) go func() { for number := range input { channel <- number / 2 } close(channel) }() return channel }
Top comments (0)