请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

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

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的错,为什么呀?

正在回答 回答被采纳积分+3

1回答

ccmouse 2019-03-18 21:45:41

是的。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 回复 有任何疑惑可以回复我~
  • 感觉老师您这个回答,有点不好理解。参考了一下网上的资料,是否可以这么理解:
    “for range创建了每个元素的副本,而不是直接返回每个元素的引用”
    回复 有任何疑惑可以回复我~ 2019-03-23 00:00:25
  • 我理解老师的意思是,
    for _, worker := range workers{
    		go worker.doWork()
    	}
    
    的过程中,for循环起了n个goroutine ,都是使用worker这个值,而worker这个值是用的外界的不断被for改变的值,实际goroutine执行的时候,worker已经被for循环设置为最后一个workers里面的了(worker被赋值为workers[9]),然后10个goroutine都是使用的这个worker,也形成了闭包。
    回复 有任何疑惑可以回复我~ 2020-02-05 22:44:03
  • 嗯嗯,是的
    回复 有任何疑惑可以回复我~ 2020-03-15 19:25:37
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信