Aula 27 – Tutorial Golang – Sincronização de Canais

Aula 27 – Tutorial Golang – Sincronização De Canais

Tutorial Golang - Channel Synchronization

Tutorial Golang – Channel Synchronization

Pacote Programador Fullstack

Pacote Programador Fullstack

Página principal do blog

Todas as aulas desse curso

Aula 26                        Aula 28

Redes Sociais:

facebook

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Melhore seu NETWORKING

https://digitalinnovation.one/

Participe de comunidades de desenvolvedores:

Fiquem a vontade para me adicionar ao linkedin.

E também para me seguir no https://github.com/toticavalcanti.

Código final da aula:

https://github.com/toticavalcanti

Canais do Youtube:

Toti

Backing Track / Play-Along

Código Fluente

Putz!

Vocal Techniques and Exercises

PIX para doações

PIX Nubank

PIX Nubank

Aula 27 – Tutorial Golang – Sincronização de canais

Fontes usadas para esse post:

Canais tem uma forma um pouco diferente de receber e enviar dados.

ch <- values // enviando em um canal
value := <- ch // Recebendo em um canal e atribuindo o valor
<-ch // Recebendo em um canal e descartando o resultado
close(ch) // Fechando um canal. Isso significa que o canal não pode enviar nenhum dado

Os channels funcionam parecido com as threads tradicionais, mas, ao invés das threads serem geradas pelo schedule do sistema operacional, elas são geradas pela runtime do Go.

O runtime são os códigos das bibliotecas Go.

Então, quando a gente compila um código Go, o runtime dele é compilado junto com o código, juntando os códigos do runtime com o código do nosso programa.

Parecido com o que ocorre com a JVM do java e o próprio código de um programa em java, que roda dentro do ambiente virtual JVM.

O pessoal que criou o golang, para simplificar o uso de fluxos de rotinas concorrentes, ou seja, as threads, resolveu criar o próprio gerenciador de tarefas, isto é, o schedule, que criam as próprias threads.

Por isso quem gerencia as threads em Go é a própria runtime.

Essas threads são chamadas de threads de nível de usuário, ou terra de usuário, ou threads verdes ou ainda threads leves.

Cada thread go ocupa 2kb de memória, cada thread tradicional ocupa 1 mega.

Schedule

O Schedule das threads tradicionais, ou seja, o cara responsável pelo cronograma de tarefas dentro do SO, pode usar o escalonamento preemptivo ou cooperativo.

O preemptivo é o que define um tempo de processamento para cada tarefa, não importando se a tarefa foi ou não concluída, o processamento é interrompido em função de outra tarefa.

O cooperativo, a tarefa é processada o máximo possível, só liberando o processador caso termine de executar, as tarefas gerenciam seu próprio ciclo de vida.

Depois de ser atribuído a um worker, cabe à tarefa se ela deve ou não ser liberada de um trabalhador, ou seja, devolver o controle ao Schedule.

O trabalho do agendador é apenas atribuir tarefas a qualquer worker que esteja livre.

Go usa por padrão o escalonamento cooperativo, mas também é possível usar o escalonamento preemptivo.

Esse escalonamento pode levar um processo a monopolizar os recursos de processamento.

Para evitar isso, o Go estabelece algumas regras, por exemplo, se tiver que acessar disco, ou fazer qualquer outra ação não bloqueante, vai para a próxima goroutine que precisa ser processada.

Ou se, por exemplo, tiver que fazer uma chamada externa a uma API, também faz o processamento de outra thread (goroutine), se tiver que dá um timeout ou um sleep, mesma coisa.

Channel synchronization

Os canais podem ser usados para sincronizar goroutines.

Um canal pode fazer uma goroutine esperar até terminar.

O canal pode então ser usado para notificar uma 2ª goroutine.

Imagine que você tenha várias goroutines.

Às vezes, uma goroutine precisa ser concluída antes que você possa iniciar a próxima (síncrona).

Isso pode ser resolvido com canais.

Podemos usar canais para sincronizar a execução entre essas goroutines.

Aqui está um exemplo de uso de um recebimento de bloqueio para esperar que uma goroutine termine.

Para aguardar a conclusão de várias goroutines, você pode usar um WaitGroup.

Esta é a função que vamos executar em uma goroutine.

O canal done será notificado que o trabalho da função foi terminado.


func task(done chan bool) {
    fmt.Print("working...")
    time.Sleep(time.Second)
    fmt.Println("done")
    done <- true
}

Na main(), é inicializada uma goroutine chamada task, dando a ela o canal done que recebe a notificação.

O código <-done, bloqueia até receber uma notificação do task no canal done.


func main() {
	done := make(chan bool, 1)
	go task(done)
	<-done
}

Se você remover a linha <-done do código, o programa seria encerrado antes mesmo da task iniciar.

Código completo


package main

import (
	"fmt"
	"time"
)

func task(done chan bool) {
	fmt.Print("working...")
	time.Sleep(time.Second)
	fmt.Println("done")
	done <- true
}

func main() {
	done := make(chan bool, 1)
	go task(done)
	<-done
}

Execute o programa com:

go run channel_synchronization.go

Saída:
working…done

Código 02 completo


package main

import (
	"fmt"
	"time"
)

func task(taskId int, msg chan int) {
	for res := range msg {
		fmt.Println("Task: ", taskId, "Message: ", res)
		time.Sleep(time.Second)
	}
}

func main() {
	msg := make(chan int)
	go task(1, msg)
	//go task(2, msg)
	//go task(3, msg)
	//go task(4, msg)
	for i := 0; i < 10; i++ {
		msg <- i
	}
}

Saída:
Task: 1 Message: 0
Task: 1 Message: 1
Task: 1 Message: 2
Task: 1 Message: 3
Task: 1 Message: 4
Task: 1 Message: 5
Task: 1 Message: 6
Task: 1 Message: 7
Task: 1 Message: 8
Task: 1 Message: 9

Sincronizando goroutines

Dá pra fazer uma goroutine esperar por outra.

Você pode fazer isso com uma instrução if e lendo o valor do canal.

No código abaixo, a goroutine task2 aguardará a finalização da goroutine task.

Observação

As goroutines (task, task2) podem ser síncronas o tempo todo enquanto são executadas simultaneamente.

Como tudo é concorrente, você ainda pode usar o thread main do seu programa ao mesmo tempo.


package main

import "fmt"
import "time"

func task(done chan bool) {
    fmt.Print("Task 1 (goroutine) running...")
    time.Sleep(time.Second)
    fmt.Println("done")

    done <- true
}

func task2() {
    fmt.Println("Task 2 (goroutine)")
}

func main() {
   done := make(chan bool, 1)
   go task(done)
   fmt.Println("Im in the main thread!")

   if <- done {
       go task2()
       fmt.Scanln()
   }
}

Executando o programa com:

go run channel_synchronization.go

Você verá a saída abaixo e o programa vai ficar esperando que você tecle qualquer coisa para encerrar, por causa do fmt.scanln().

Saída:
Im in the main thread!
Task 1 (goroutine) running…done
Task 2 (goroutine)

É isso pessoal, fico por aqui!

Até mais. 🙂

Página principal do blog

Todas as aulas desse curso

Aula 26                        Aula 28

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado e bons estudos. 😉

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>