全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

关于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 错误。调试信息显示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 的值指向切片中最后一个元素。

The val variable in the above loops is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.

解决方法

以上代码正确的写法为:

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  # goroutine  # goroutine数量  # loop  # 解决Golang中goroutine执行速度的问题  # golang goroutine顺序输出方式  # golang gin 框架 异步同步 goroutine 并发操作  # GOLANG使用Context管理关联goroutine的方法  # Golang 探索对Goroutine的控制方法(详解)  # Golang 语言控制并发 Goroutine的方法  # 是在  # 过程中  # 遍历  # 不明白  # 的是  # 应该是  # 是一个  # 有什么  # 在这里  # 说了  # 不多  # 这个问题  # 也可  # 并将  # 意识到  # 这个时候  # 作为一个  # 时就  # 种方法  # 这篇文章 


相关文章: 企业网站制作公司网页,推荐几家专业的天津网站制作公司?  建站主机无法访问?如何排查域名与服务器问题  广东企业建站网站优化与SEO营销核心策略指南  网站建设制作需要多少钱费用,自己做一个网站要多少钱,模板一般多少钱?  浅谈Javascript中的Label语句  如何实现建站之星域名转发设置?  网站专业制作公司,网站编辑是做什么的?好做吗?工作前景如何?  早安海报制作网站推荐大全,企业早安海报怎么每天更换?  ,怎么在广州志愿者网站注册?  长沙做网站要多少钱,长沙国安网络怎么样?  Swift中swift中的switch 语句  网站制作价目表怎么做,珍爱网婚介费用多少?  如何高效生成建站之星成品网站源码?  如何在建站宝盒中设置产品搜索功能?  香港服务器租用费用高吗?如何避免常见误区?  如何生成腾讯云建站专用兑换码?  网站制作软件有哪些,制图软件有哪些?  宝华建站服务条款解析:五站合一功能与SEO优化设置指南  湖北网站制作公司有哪些,湖北清能集团官网?  电视网站制作tvbox接口,云海电视怎样自定义添加电视源?  番禺网站制作公司哪家值得合作,番禺图书馆新馆开放了吗?  齐河建站公司:营销型网站建设与SEO优化双核驱动策略  如何通过老薛主机一键快速建站?  如何选择高效稳定的ISP建站解决方案?  C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)  如何用PHP快速搭建高效网站?分步指南  如何在VPS电脑上快速搭建网站?  如何选择适合PHP云建站的开源框架?  创业网站制作流程,创业网站可靠吗?  沈阳个人网站制作公司,哪个网站能考到沈阳事业编招聘的信息?  建站主机是否属于云主机类型?  极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?  如何彻底删除建站之星生成的Banner?  如何用搬瓦工VPS快速搭建个人网站?  子杰智能建站系统|零代码开发与AI生成SEO优化指南  西安制作网站公司有哪些,西安货运司机用的最多的app或者网站是什么?  如何用5美元大硬盘VPS安全高效搭建个人网站?  建站上市公司网站建设方案与SEO优化服务定制指南  h5网站制作工具有哪些,h5页面制作工具有哪些?  如何快速生成橙子建站落地页链接?  导航网站建站方案与优化指南:一站式高效搭建技巧解析  c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】  专业网站制作服务公司,有哪些网站可以免费发布招聘信息?  如何用wdcp快速搭建高效网站?  如何快速搭建安全的FTP站点?  Thinkphp 中 distinct 的用法解析  高端云建站费用究竟需要多少预算?  郑州企业网站制作公司,郑州招聘网站有哪些?  建站之星安装提示数据库无法连接如何解决?  如何通过商城免费建站系统源码自定义网站主题? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。