diff options
| -rw-r--r-- | main.go | 21 |
1 files changed, 21 insertions, 0 deletions
@@ -31,6 +31,15 @@ func main() { }) } +// game sends a [Ball] struct to successive players, modeled as +// goroutines, in a ring: imagine the players are the vertices of a +// directed graph in the form of a polygon, and one vertex sends the +// ball, unidirectionally, to its adjacent vertex. One of the vertices +// (the main goroutine) acts as a referee determining whether the game +// should stop (on the basis of a timeout or else some other +// criterion.) +// +// This is a generalization of the ping-pong example. func game(numSecs, numPlayers int) { var wg sync.WaitGroup @@ -41,6 +50,9 @@ func game(numSecs, numPlayers int) { players[i+1] = player(i+1, &wg, players[i]) } + // Implicitly, there is yet another goroutine here that is + // measuring the criterion for termination: the t channel + // effectively plays the role of a "done" channel here. t := time.Tick(time.Duration(numSecs) * time.Second) players[0] <- Ball{} @@ -77,6 +89,10 @@ loop: // output. Both channels are technically receive-only, but making them // bidirectional simplifies making them members of a slice which // contains a bidirectional channel. +// +// Player goroutines use the provided id parameter for logging when +// they start and stop, which helps sanity-check whether all launched +// goroutines have, in fact, exited. func player(id int, wg *sync.WaitGroup, input chan Ball) chan Ball { out := make(chan Ball) @@ -91,6 +107,11 @@ func player(id int, wg *sync.WaitGroup, input chan Ball) chan Ball { for b := range input { b.hits++ fmt.Printf("(%d) %d\n", id, b.hits) + + // Introduce a mock "blocking" element to the + // computation. This could represent, for + // example, the interval of time through which + // the ball makes contact with the paddle. time.Sleep(100 * time.Millisecond) out <- b |
