Skip to content

Instantly share code, notes, and snippets.

@WhisperingChaos
Last active October 16, 2018 02:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WhisperingChaos/18c64a08afc7d930530d8a91c0e2cd49 to your computer and use it in GitHub Desktop.
Save WhisperingChaos/18c64a08afc7d930530d8a91c0e2cd49 to your computer and use it in GitHub Desktop.
Alternative to Synchronization queues in Golang
package main
import (
"fmt"
"math/rand"
"strconv"
"sync"
"time"
)
// An alternative to "Synchronization queues in Golang" (https://medium.com/golangspec/synchronization-queues-in-golang-554f8e3a31a4)
// See goplayground ()
// No happens before relationship that gaurnetts message order.
// Primary differences:
// work and play in separate go routines. Important as when waiting for tother player, when using buffered channel , o goroutines are executing reclaims storage.
// eliminated the stat messages
// termination message implemented as separate channel
// ensured to start timer to impose ordering constraint.
// downside of happens before - more work for scheduler as goroutines transition from running to waiting to running again.
func main() {
testerCnt := 10
testers := make(chan play, testerCnt) // buffered channels can comfortably scale memory beyond 2*10+6 players ~3-4GB free
go playerGen("Tester", testers, testerCnt)
progrmCnt := 10
progrms := make(chan play, progrmCnt)
go playerGen("Programmer", progrms, progrmCnt)
term := make(chan bool)
go playersPair(testers, progrms, 0, term)
<-term
}
func playerGen(role string, p chan play, max int) {
for i := 0; i < max; i++ {
plr := player{}
plr.mePlay(role+"_"+strconv.Itoa(i+1), p)
}
}
func playersPair(pque1 <-chan play, pque2 <-chan play, gameTot int, term chan<- bool) {
defer func() { term <- true }()
var inc int
if gameTot == 0 {
// unlimited number of games
inc = -1
}
for i := inc; i < gameTot; i++ {
i += inc
fmt.Println()
pque1sel := pque1
pque2sel := pque2
var p1 play
select {
case p1 = <-pque1sel:
pque1sel = nil
case p1 = <-pque2sel:
pque2sel = nil
}
var p2 play
select {
case p2 = <-pque1sel:
pque1sel = nil
case p2 = <-pque2sel:
pque2sel = nil
}
var playing sync.WaitGroup
playing.Add(2)
p1.play(&playing)
p2.play(&playing)
playing.Wait()
}
}
type play interface {
play(playing *sync.WaitGroup)
}
type player struct {
role string
que chan play
}
func (p *player) mePlay(role string, que chan play) {
p.role = role
p.que = que
p.work()
}
func (p *player) play(playing *sync.WaitGroup) {
start := make(chan bool)
go func(p *player, start chan<- bool, playing *sync.WaitGroup) {
fmt.Printf("%s starts\n", p.role)
start <- true
time.Sleep(time.Duration(rand.Intn(1999)+1) * time.Millisecond)
fmt.Printf("%s ends\n", p.role)
playing.Done()
p.work()
}(p, start, playing)
<-start
}
func (p *player) work() {
start := make(chan bool)
go func(p *player, startedWorking chan<- bool) {
// start work for this player before starting another player
// enforces runtime ordering that might otherwise unfairly reorder goroutines
// whose timers expire at or near the same instant. Note - due to random duration
// a more recent player's work timer may expire nearly before, nearly
// after, or at the same time when compared to a previous player that's
// already working.
startedWorking <- true
time.Sleep(time.Duration(rand.Intn(9999)+1) * time.Millisecond)
p.que <- p
}(p, start)
<-start
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment