Created
August 11, 2017 18:43
-
-
Save johnsogg/8d4ec56b8a1a2fe2fe5b2d4a4ad75e45 to your computer and use it in GitHub Desktop.
Demonstration of how to coordinate many long-running goroutines using channels
This file contains 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 ( | |
"bufio" | |
"fmt" | |
"math/rand" | |
"os" | |
"time" | |
) | |
func main() { | |
// Keeping jobs in a map so we can look them up later. We happen to | |
// be using int keys, but they could be strings or whatever else. | |
jobs := make(map[int]*longJob) | |
// number of jobs. play with it. it can be larger than the number of | |
// goroutines, but there will be blocking. | |
n := 100 | |
// create the jobs, giving each their own chan chan int. | |
for i := 0; i < n; i++ { | |
job := &longJob{i, make(chan chan int, 1)} | |
jobs[i] = job | |
} | |
fmt.Printf("Made %d channels\n", len(jobs)) | |
// start them all | |
for _, job := range jobs { | |
job.start() | |
} | |
// Wait for user input, and when we get it, send quit message to all | |
// jobs | |
bufio.NewReader(os.Stdin).ReadBytes('\n') | |
rsvp := make(chan int, 1) | |
// write the each job's quit channel giving them the rsvp | |
// channel. they'll write their ID to it when they finish. | |
for _, job := range jobs { | |
job.quit <- rsvp | |
} | |
// listen for the right number of responses | |
for i := 0; i < n; i++ { | |
jobId := <-rsvp | |
delete(jobs, jobId) | |
fmt.Printf("\n%d: Job %d is done. %d jobs still running.\n", i, jobId, len(jobs)) | |
} | |
fmt.Printf("\n\nAll jobs complete. Closing rsvp channel.\n") | |
close(rsvp) | |
} | |
type longJob struct { | |
jobId int // which job this represents | |
quit chan chan int // when job gets msg on this channel it writes done to the channel and quits | |
} | |
func (j *longJob) start() { | |
fmt.Printf("Starting job %d...\n", j.jobId) | |
go j.process() | |
fmt.Printf("Started job %d.\n", j.jobId) | |
} | |
func (j *longJob) process() { | |
naptime := time.Duration(rand.Int63n(6000)) * time.Millisecond | |
keepRunning := true | |
for keepRunning { | |
fmt.Printf("[%d] ", j.jobId) | |
time.Sleep(naptime) | |
select { | |
case rsvp := <-j.quit: | |
fmt.Printf("[%d quits] ", j.jobId) | |
close(j.quit) | |
keepRunning = false | |
rsvp <- j.jobId | |
default: | |
// the default case is necessary, otherwise it would block on | |
// the <-j.quit case for ever. | |
} | |
} | |
fmt.Printf("[%d complete] ", j.jobId) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment