Jantar dos Filósofos - golang

Jantar dos Filosofos
Filosofos jantando


Definição do problema:


Um dos problemas clássicos de coordenação mais conhecidos é o jantar dos filósofos, proposto inicialmente por Dijkstra [Raynal, 1986; Ben-Ari, 1990]. Neste problema, um grupo de cinco filósofos alternam suas vidas entre pensar e comer:


Cinco filósofos estão sentados em uma mesa redonda para janta, e os mesmos se alimentam e pensam durante o jantar. Cada filósofo tem um prato com macarrão à sua frente. Cada prato possui um garfo para pegar o macarrão. O macarrão está muito escorregadio e, para que um filósofo consiga comer, será necessário utilizar dois garfos.


Lembre-se que é apenas uma analogia. Nesse sentido, cada filósofo alterna entre duas tarefas: comer ou pensar. Quando um filósofo fica com fome, ele tenta pegar os garfos à sua esquerda e à sua direita; um de cada vez, independente da ordem. Caso ele consiga pegar dois garfos, ele come durante um determinado tempo e depois recoloca os garfos na mesa. Em seguida ele volta a pensar.


O problema do jantar dos filósofos é representativo de uma grande classe de problemas de sincronização entre vários processos e vários recursos sem usar um coordenador central. Resolver o problema do jantar dos filósofos consiste em encontrar uma forma de coordenar suas atividades de maneira que todos os filósofos consigam meditar e comer.

                
package main

import (
"hash/fnv"
"log"
"math/rand"
"os"
"sync"
"time"
)

// Number of philosophers is simply the length of this list.
var ph = []string{"Aristóteles", "Platão", "Sócrates", "Pitágoras", "Demócrito"}

const hunger = 3                // Numero de vezes que cada filósofo vai comer
const think = time.Second / 1000 // Tempo de pensamento
const eat = time.Second / 1000   // Tempo para comer

var fmt = log.New(os.Stdout, "", 0)

var dining sync.WaitGroup

func diningProblem(phName string, dominantHand, otherHand *sync.Mutex) {
fmt.Println(phName, "Sentado")
//time.Sleep(2* time.Second)
h := fnv.New64a()
h.Write([]byte(phName))
rg := rand.New(rand.NewSource(int64(h.Sum64())))
rSleep := func(t time.Duration) {
    time.Sleep(t/2 + time.Duration(rg.Int63n(int64(t))))
}
for h := hunger; h > 0; h-- {
    fmt.Println(phName, "Com fome")
    dominantHand.Lock() // Pega o garfo
    otherHand.Lock()
    fmt.Println(phName, "Comendo")
    rSleep(eat)
    time.Sleep(2* time.Second)
    dominantHand.Unlock() // Larga o garfo
    otherHand.Unlock()
    fmt.Println(phName, "Pensando")
    rSleep(think)
}
fmt.Println(phName, "Satisfeito")
dining.Done()
fmt.Println(phName, "Sai da mesa")
}

func main() {
fmt.Println("Mesa vazia")
dining.Add(5)
fork0 := &sync.Mutex{}
forkLeft := fork0
for i := 1; i < len(ph); i++ {
    forkRight := &sync.Mutex{}
    go diningProblem(ph[i], forkLeft, forkRight)
    forkLeft = forkRight
}
go diningProblem(ph[0], fork0, forkLeft)
dining.Wait() // Espera os filosofos terminarem
fmt.Println("Mesa vazia")
                    }
                
            
Jantar dos Filosofos
Problema dos Filosofos

Explicação do código:


Para a realização do problema foi utilizada a linguagem Go (Golang da google), para isso foi necessario utilizar duas bibliotecas importantes como a "os" e "sync".

A biblioteca "os" fornece uma interface independente da plataforma para a funcionalidade do sistema operacional. O design é semelhante ao Unix, embora o tratamento de erros seja semelhante ao Go-like.

Já a biblioteca "sync" fornece dados primitivos de sincronização, como ‎bloqueios de exclusão mútua‎ (mutual exclusion locks). Além dos tipos Once e WaitGroup, a maioria é destinada a ser usada por rotinas de biblioteca de baixo nível. A sincronização de nível superior é melhor feita através de canais e comunicação.

No começo do código podemos ver a utilização a biblioteca "sync" onde é utilizado o método sync.WaitGroup que é utilizada para chamar as funções Add, Done e Wait. O Wait() serve para aguardar um bloco ou blocos até que o contador WaitGroup seja zero como visto nas linhas 49 com Done e 65 com Wait, ou seja, senão tivessemos acrescido o valor de WaitGroup com o Add o código nem executaria a função diningProblem(). Os métodos Lock() (linha 86) e Unlock() (linha 90) serve para ele manter o estado atual sem que as proximas mudanças atrapalhem no valor recebido, ou seja, sem os métodos o programa poderia sobrepor valores antes das modificações anteriores, por exemplo, ele bloqueia o valor de otherHand feito isso não haverá mudança de valor até que o método Unlock seja chamado.