I have the following code to spawn a number of Goroutines. Each Goroutine does a variable amount of work (simulated here by different loop lengths) and then atomically sets a variable in its job structure to signal its done. The main() Goroutine checks all the job structures for completion using compare_and_swap. This code is racy, the variable finish
goes beyond numjobs
, and I don't understand why.
I know I can use waitgroups to achieve this, however, I want to understand why this fails.
type job struct {
id int
done uint32
loops int
}
const (
numjobs int = 10000
)
func (j *job) work() {
for k := 0; k < j.loops; k {
}
fmt.Printf("Ending job %d\n", j.id)
atomic.StoreUint32(&j.done, 1)
}
func main() {
// Memory for jobs
jobs := make([]job, numjobs)
// Kick off the jobs, each running a random number of loops
for i := 0; i < numjobs; i {
jobs[i].id = i
jobs[i].loops = rand.Intn(1000000)
go (&jobs[i]).work()
}
// Track when all jobs are done
finish := 0
for finish != numjobs {
for _, job := range jobs {
if atomic.CompareAndSwapUint32(&job.done, 1, 0) {
finish
}
}
fmt.Printf("Finished %d jobs\n", finish)
}
}
CodePudding user response:
You are updating a copy of the job
. Try this:
for job := range jobs {
if atomic.CompareAndSwapUint32(&jobs[job].done, 1, 0) {
finish
}
}