大佬教程收集整理的这篇文章主要介绍了Golang 中 for-loop 和 goroutine 的问题,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
最近在学习MIT的分布式课程6.824的过程中,使用Go实现Raft协议时遇到了一些问题。参见如下代码:
for i := 0; i < len(rf.peers); i++ { DPrintf("i = %d",i) if i == rf.me { DPrintf("skipping myself #%d",rf.mE) conTinue } go func() { DPrintf("len of rf.peers = %d",len(rf.peers)) DPrintf("server #%d sending request Vote to server %d",rf.me,i) reply := &requestVoteReply{} ok := rf.sendrequestVote(i,args,reply) if ok && reply.VoteGranted && reply.Term == rf.currentTerm { rf.VoteCount++ if rf.VoteCount > len(rf.peers)/2 { rf.winElectionCh <- true } } }() }
其中,peers切片的长度为3,因此最高下标为2,在非并行编程中代码中的for-loop应该是很直观的,我当时并没有意识到有什么问题。可是在调试过程中,一直在报 index out of bounds @L_874_8@。调试信息显示i的值为3,当时就一直想不明白循环条件明明是 i < 2
,怎么会变成3呢。
虽然不明白发生了什么,但知道应该是循环中引入的 goroutIne 导致的。经过Google,发现Go的wiki中就有一个页面 Common Mistake - Using goroutines on loop iterator variables 专门提到了这个问题,看来真的是很 common 啊,笑哭~
初学者经常会使用如下代码来并行处理数据:
for val := range values { go val.Mymethod() }
或者使用闭包(closure):
for val := range values { go func() { fmt.Println(val) }() }
这里的问题在于 val 实际上是一个遍历了切片中所有数据的单一变量。由于闭包只是绑定到这个 val 变量上,因此极有可能上面的代码的运行结果是所有 goroutIne 都输出了切片的最后一个元素。这是因为很有可能当 for-loop 执行完之后 goroutIne 才开始执行,这个时候 val 的值指向切片中最后一个元素。
以上代码正确的写法为:
for val := range values { go func(val interface{}) { fmt.Println(val) }(val) }
在这里将 val 作为一个参数传入 goroutIne 中,每个 val 都会被独立计算并保存到 goroutIne 的栈中,从而得到预期的结果。
另一种方法是在循环内定义新的变量,由于在循环内定义的变量在循环遍历的过程中是不共享的,因此也可以达到同样的效果:
for i := range valslice { val := valslice[i] go func() { fmt.Println(val) }() }
对于文章开头提到的那个问题,最简单的解决方案就是在循环内加一个临时变量,并将后面 goroutIne 内的 i 都替换为这个临时变量即可:
server := i
,
以上是大佬教程为你收集整理的Golang 中 for-loop 和 goroutine 的问题全部内容,希望文章能够帮你解决Golang 中 for-loop 和 goroutine 的问题所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。