←back to thread

159 points mpweiher | 1 comments | | HN request time: 0.269s | source
Show context
ricardobeat ◴[] No.43671958[source]
Strange to go all this length without mentioning the approaches that solve the problem in that first example:

1. send a close message on the channel that stops the goroutine

2. use a Context instance - `ctx.Done()` returns a channel you can select on

Both are quite easy to grasp and implement.

replies(2): >>43671984 #>>43672081 #
jtolds ◴[] No.43671984[source]
Hi! No, I think you've misunderstood the assignment. The example posits that you have a "game" running, which should end when the last player leaves. While only using channels as a synchronization primitive (a la CSP), at what point do you decide the last player has left, and where and when do you call close on the channel?
replies(5): >>43672077 #>>43672431 #>>43672954 #>>43673132 #>>43675471 #
1. taberiand ◴[] No.43675471[source]
I don't think there's much trouble at all fixing the toy example by extending the message type to allow communication of the additional conditions, and I think my changes are better than the alternative of using a mutex. Have I overlooked something?

Assuming the number of players are set up front, and players can only play or leave, not join. If the expectation is that players can come and go freely and the game ends some time after all players have left, I believe this pattern can still be used with minor adjustment

(please overlook the pseudo code adjustments, I'm writing on my phone - I believe this translates reasonably into compilable Go code):

  type Message struct {
    exit bool
    score    int
    reply chan bool
  }

  type Game struct {
    bestScore int
    players int // > 0
    messages    chan Message
  }

  func (g *Game) run() {
    for message := range g.messages {
      if message.exit {
        g.players = g.players - 1;

        if g.players == 0 {
          return
        }
        continue
      }
  
      if g.bestScore < 100 && g.bestScore < message.score {
        g.bestScore = message.score
      }

      acceptingScores := g.bestScore < 100

      message.reply <- acceptingScores
    }
  }

  func (g *Game) HandlePlayer(p Player) error {
    for {
      score, err := p.NextScore()
      if err != nil {
        g.messages <- { exit: true
      }
      return err
    }
    g.messages <- { score, reply }
    if not <- reply {
       g.messages <- { exit: true }
       return nil
      }
    }
  }