遍历方式一旦换成range的方式遍历,就会报deadlock的错,为什么呀?

来源:11-2 使用Channel等待任务结束

Antgan

2019-03-15

package main

import (
	"fmt"
	"sync"
)

type worker struct {
	id int
	c  chan int
	wg *sync.WaitGroup
}

func createWorker(id int, wg *sync.WaitGroup) worker {
	c := make(chan int)
	return worker{id, c, wg}
}

func (worker *worker) doWork() {
	for v := range worker.c {
		fmt.Printf("Worker %d do work: %c\n", worker.id, v)
		worker.wg.Done()
	}
}

func main() {
	var wg sync.WaitGroup
	var workers [10] worker
	//init worker
	for i := 0; i < 10; i++ {
		workers[i] = createWorker(i, &wg)
	}
	//Do work
	wg.Add(10)
	//for i := 0; i < 10; i++ {
	//	go workers[i].doWork()
	//}
	//一旦换成range的方式遍历,就会报deadlock的错,为什么呀?
	for _, worker := range workers{
		go worker.doWork()
	}

	//Send data
	for i := 0; i < 10; i++ {
		workers[i].c <- 'a' + i
	}

	wg.Wait()
}

一旦换成range的方式遍历,就会报deadlock的错,为什么呀?

写回答

1回答

ccmouse

2019-03-18

是的。go的for range有一个坑,就是这个worker整个循环过程中只有一个,它不断的被赋值为workers里面的worker。使用了go worker.doWork(),异步的调用了doWork函数,导致这个for循环很快走完,worker被赋值为workers[9]。我们可以把doWork里的worker打印出来看一下,的确是10次对workers[9]的调用。


用range的做法是利用函数传参来复制一份

 for _, w:= range workers{
  go func(w worker) {
    w.doWork()
  }(w)
 }

这样就可以。

0
4
尼克2018
回复
乐只君子
是的,之前goroutine那章节里有一次提到过,for循环里 用go func(i int){xxx}(i),闭包的问题。
2022-02-14
共4条回复

Google资深工程师深度讲解Go语言 由浅入深掌握Go语言

语法+分布式爬虫实战 为转型工程师量身打造

5995 学习 · 1909 问题

查看课程