Simulating waitgroups using Go atomics

huangapple go评论79阅读模式
英文:

Simulating waitgroups using Go atomics

问题

我有以下代码来生成一些Goroutine。每个Goroutine执行不同数量的工作(在这里通过不同的循环长度模拟),然后原子地设置其作业结构中的变量以表示完成。主Goroutine使用compare_and_swap检查所有作业结构是否完成。这段代码存在竞争条件,变量finish超过了numjobs,我不明白为什么会这样。

我知道可以使用waitgroups来实现这一点,但是我想了解为什么这种方法失败了。

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)
	}

}
英文:

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 &lt; j.loops; k++ {

	}
	fmt.Printf(&quot;Ending job %d\n&quot;, j.id)
	atomic.StoreUint32(&amp;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 &lt; numjobs; i++ {
		jobs[i].id = i
		jobs[i].loops = rand.Intn(1000000)
		go (&amp;jobs[i]).work()
	}

	// Track when all jobs are done
	finish := 0
	for finish != numjobs {
		for _, job := range jobs {
			if atomic.CompareAndSwapUint32(&amp;job.done, 1, 0) {
				finish++
			}
		}
		fmt.Printf(&quot;Finished %d jobs\n&quot;, finish)
	}

}

答案1

得分: 2

你正在更新 job 的副本。尝试这样做:

       for job := range jobs {
            if atomic.CompareAndSwapUint32(&jobs[job].done, 1, 0) {
                finish++
            }
        }
英文:

You are updating a copy of the job. Try this:

       for job := range jobs {
            if atomic.CompareAndSwapUint32(&amp;jobs[job].done, 1, 0) {
                finish++
            }
        }

huangapple
  • 本文由 发表于 2022年8月19日 06:22:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/73410031.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定