Last active
October 16, 2018 02:31
-
-
Save WhisperingChaos/18c64a08afc7d930530d8a91c0e2cd49 to your computer and use it in GitHub Desktop.
Alternative to Synchronization queues in Golang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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