Go   发布时间:2022-04-09  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Golang 中 for-loop 和 goroutine 的问题大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

00. 背景

最近在学习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 错误。调试信息显示i的值为3,当时就一直想不明白循环条件明明是 i < 2怎么会变成3呢。

01. 调查

然不明白发生了什么,但知道应该是循环中引入的 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 的值指向切片中最后一个元素。

02. 解决方法

以上代码正确的写法为:

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,请注明来意。