Most active commenters
  • (3)

←back to thread

159 points mpweiher | 16 comments | | HN request time: 0.837s | source | bottom
1. 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 #
2. 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 #
3. blablabla123 ◴[] No.43672077[source]
I don't think channels should be used for everything. In some cases I think it's possible to end up with very lean code. But yes, if you have a stop channel for the other stop channel it probably means you should build your code around other mechanisms.

Since CSP is mentioned, how much would this apply to most applications anyway? If I write a small server program, I probably won't want to write it on paper first. With one possible exception I never heard of anyone writing programs based on CSP (calculations?)

replies(1): >>43674842 #
4. sapiogram ◴[] No.43672081[source]
You've misunderstood the example. The `scores` channel aggregates scores from all players, you can't close it just because one player leaves.

I'd really, really recommend that you try writing the code, like the post encourages. It's so much harder than it looks, which neatly sums up my overall experience with Go channels.

replies(3): >>43672616 #>>43673034 #>>43673193 #
5. aflag ◴[] No.43672431[source]
Naive question, can't you just have a player count alongside the best score and leave when that reaches 0?
replies(1): >>43674754 #
6. politician ◴[] No.43672616[source]
It’s not entirely clear whether the author is describing a single or multiplayer game.

Among the errors in the multiplayer case is the lack of score attribution which isn’t a bug with channels as much as it’s using an int channel when you needed a struct channel.

replies(2): >>43672856 #>>43673866 #
7. jeremyjh ◴[] No.43672856{3}[source]
The whole point is that it is multiplayer.
8. angra_mainyu ◴[] No.43672954[source]
Haven't read the article but it sounds like a waitgroup would suffice.
9. ◴[] No.43673034[source]
10. ◴[] No.43673132[source]
11. ricardobeat ◴[] No.43673193[source]
In both examples, the HandlePlayer for loop only exits if .NextScore returns an error.

In both cases, you’d need to keep track of connected players to stop the game loop and teardown the Game instance. Closing the channel during that teardown is not a hurdle.

What am I missing?

replies(1): >>43674082 #
12. ◴[] No.43673866{3}[source]
13. guilhas ◴[] No.43674082{3}[source]
I think nothing

I was thinking the same, the only problem is the author not keeping track of players

On HandlePlayer return err you would decrement a g.players counter, or something, and in the Game.run just do if !g.hasPlayers() break close(g.scores)

The solution requires nothing special, just basic logic that should probably be there anyway

If anything this post shows that mutexes are worse, by making bad code work

14. jtolds ◴[] No.43674754{3}[source]
Adding an atomic counter is absolutely a great solution in the real world, definitely, and compare and swap or a mutex or similar totally is what you want to do. In fact, that's my point in that part of the post - you want an atomic variable or a mutex or something there. Other synchronization primitives are more useful than sticking with the CSP idea of only using channels for synchronization.
15. franticgecko3 ◴[] No.43674842{3}[source]
> Since CSP is mentioned, how much would this apply to most applications anyway? If I write a small server program, I probably won't want to write it on paper first. With one possible exception I never heard of anyone writing programs based on CSP (calculations?)

CSP is really in the realm of formal methods. No you wouldn't formulate your server program as CSP, but if you were writing software for a medical device, perhaps.

This is the FDR4 model checker for CSP, it's a functional programming language that implements CSP semantics and may be used to assert (by exhaustion, IIRC) the correctness of your CSP model.

https://cocotec.io/fdr/

I believe I'm in the minority of Go developers that have studied CSP, I fell into Go by accident and only took a CSP course at university because it was interesting, however I do give credit to studying CSP for my successes with Go.

16. 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
      }
    }
  }