Go: Processos e Concorrência

De Aulas
Revisão de 15h44min de 21 de outubro de 2021 por Admin (discussão | contribs) (→‎Multifilhos)

Afluentes: Sistemas Distribuídos e Mobile

Concorrência

A linguagem de programação Go trabalha com concorrência usando as gorotines. Elas exencutam funções paralelamente. É mais ou menos o conceito de threads, mas são mais leves, pois o custo da criação de gorotines é menor do que se comparado a uma thread.

Vantagens das Gorotines
  • Mais leves computacionamento, ocupando menos memória na pilha e a pilha pode alterar seu tamanho conforme a necessidade da aplicação. As threads possuem tamanho de pilha fixo;
  • As gorotines são multiplexadas para um número menor de threads do Sistema Operacional. Ou seja, pode haver uma única thread no programa com várias gorotines executando. Caso uma thread seja bloqueada por uma goritne, as outras gorotines que estão nela podem ser movidas para outra thread;
  • Goroutines se comunicam por meio de canais, que previnem que race conditions aconteçam ao acessar memória compartilhada.

Para chamar uma função de forma concorrente no Go, basta chamá-la com a palavra chave go na frente:

package main
 
import (  
    "fmt"
)
 
func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    // Vamos dar uma pausa aqui senão o programa pode terminar
    // antes de executar a gorotine
    time.Sleep(1 * time.Second)
    fmt.Println("main function")
}


Subprocessos

 1package main
 2
 3import (
 4	"fmt"
 5	"math/rand"
 6	"sync"
 7	"time"
 8)
 9
10func child(wg *sync.WaitGroup) {
11	defer wg.Done()                   // decrementa o contador de processo
12	fmt.Println("Luke: Noooooo.....") // imprime na tela
13	for i := 0; i < 5; i++ {          // executa o laço 5 vezes
14		fmt.Println("Luke: No!")                        // imprime n a tela
15		d := rand.Intn(2000)                            // retorna um número randômico de 0 a 1999
16		time.Sleep(time.Duration(d) * time.Millisecond) // aguarda um tempo
17	}
18	fmt.Println("Luke Skywalker falls.") // imprime na tela
19}
20
21func main() {
22	var wg sync.WaitGroup                           // wg é tilizado para sincronizar os processos
23	wg.Add(1)                                       // incrementa o contador de processos
24	go child(&wg)                                   // chama a rotina child paralelamente
25	fmt.Println("Vader: Luke! I am your father...") // imprime na tela
26	for i := 0; i < 5; i++ {                        // executa o laço 5 vezes
27		fmt.Println("Vader: Yes!")                      // imprime na tela
28		d := rand.Intn(2000)                            // retorna um número randômico de 0 a 1999
29		time.Sleep(time.Duration(d) * time.Millisecond) // aguarda um tempo
30	}
31	wg.Wait()                         // aguarda os subprocessos terminarem
32	fmt.Println("Dart Vader leaves.") // imprime na tela
33}

Comunicação entre Processos

A comunicação entre processos no go é feito por canais. Eles são comparados aos pipes na linguagem C. Cada canal tem um tipo associado. chan T é um canal do tipo T.

// Canal a do tipo int
a := make(chan int)

Para usar o canal, usamos uma seta direcional

// estamos lendo do canal a e jogando para a variável data
data := <- a

// estamos escrevendo as informações de data para o canal a
a <- data

Envios e recebimentos de informações pelos canais são bloqueantes, ou seja. Quando você envia algo, a função aguarda até que outra gorotine receba o dado. Da mesma forma funciona o envio. Isso também é últim para sincronização de gorotines.

Exemplo de comunicação
 1package main
 2
 3import (
 4	"fmt"
 5	"time"
 6)
 7
 8func child(fc chan string, cc chan string) {
 9	msg := <-cc                                   // aguarda a mensagem do pai
10	fmt.Println("CHILD: Father says " + msg)      // imprime na tela
11	time.Sleep(2 * time.Second)                   // aguarda 2 segundos
12	fmt.Println("CHILD: I will response just ok") // imprime na tela
13	fc <- "ok!"                                   // envia a resposta pelo canal do pai
14}
15
16func main() {
17	fatherChannel := make(chan string)                      // canal de comunicação do pai
18	childCHannel := make(chan string)                       // canal de comunicação do filho
19	go child(fatherChannel, childCHannel)                   // chama a rotina child paralelamente
20	fmt.Println("FATHER: I will say hello to my child")     // imprime na tela
21	msg := "hello"                                          // cria a variável msg e atribui a string hello
22	childCHannel <- msg                                     // envia a mensagem hello pelo canal do filho
23	msg = <-fatherChannel                                   // aguarda a resposta do filho
24	fmt.Println("FATHER: I receive " + msg + " from child") // imprime na tela
25}

Multifilhos

Quando um processo principal (pai) cria vários filhos, pode ser necessário aguardar que todos eles terminem seu processamento. Para isso, usamos uma forma de sincronização para o pai aguardar os filhos fecharem.

O exemplo abaixo cria n filhos e aguarda eles terminarem. Ele utiliza o sync.WaitGroup para sincronizar os processos.

 1package main
 2
 3import (
 4	"fmt"
 5	"math/rand"
 6	"sync"
 7	"time"
 8)
 9
10func child(wg *sync.WaitGroup, id int) { // função do filho
11	defer wg.Done()          // decrementa o sincronizador
12	for i := 0; i < 5; i++ { // conta até 5
13		fmt.Println("CHILD[", id, "] ", i)              // imprime i
14		d := rand.Intn(2000)                            // pega um valor randômico
15		time.Sleep(time.Duration(d) * time.Millisecond) // pausa um tempo aleatório
16	}
17	fmt.Println("CHILD[", id, "] done...") // imprime a finalização
18}
19
20func main() { // programa principal
21	fmt.Println("START PROGRAM...") // imprime na tela
22	var wg sync.WaitGroup           // inicializa o sincronizador
23	for i := 0; i < 5; i++ {        // cria 5 filhos
24		wg.Add(1)        // adiciona um no sincronizador
25		go child(&wg, i) //cria filho
26	}
27	wg.Wait()                     // aguarda todos os filhos terminarem
28	fmt.Println("END PROGRAM...") // imprime na tela
29}