Golang for 循环中使用goroutine

最近在开发过程中遇到问题,追踪了很久后发现是golang的经典问题,在for循环中使用了goroutine,在goroutine中使用了for循环的参数。

问题现象:

在使用rabbitmq进行数据传递时,发送端在一次循环中发送了8000条id不同的数据到rabbitmq的队列中,接收端监听该队列并从rabbitmq中取数据。接收到的数据在程序中处理后写入数据库,结果发现数据中并没有写入8000条数据。最后定位原因为:在接收数据时在for循环中使用go协程,导致同时收到两条数据时,协程都是使用的后一条数据,入库因为是同一条数据,导致主键重复,插入失败,所以数据库中没有8000条数据。错误代码大致如下:


1

2

3

4

5


for d := range msgs {

go func() {

handler(d)

}()

}

用一个简单的程序模拟该错误为:


1

2

3

4

5

6

7

8

9

10

11

12

13

14


package main

import (

"fmt"

"time"

)

func main() {

for i := 0; i < 10; i++ {

go func() {

fmt.Println(i)

}()

}

time.Sleep(2 * time.Second)

}

输出为:

7
10
10
10
10
10
10
10
10
10

问题解析:

闭包go协程里面引用的是变量i的地址;所有的go协程启动后等待调用,在上面的协程中,部分协程很可能在for循环完成之后才被调用,所以输出结果很多都是10;正常输出最多到9哦

解决方法一

通过参数传递数据到协程


1

2

3

4

5


for i := 0; i < 10; i++ {

go func(data int) {

fmt.Println(data)

}(i)

}

解决方法二

在for循环中加一个临时变量tmp,每次将i的值赋值给tmp,然后将tmp通过参数传进协程。

此方法可以解决不能通过参数传递数据的情况(某些第三方库不能传参数)


1

2

3

4

5

6


for i := 0; i < 10; i++ {

tmp := i

go func(data int) {

fmt.Println(data)

}(tmp)

}

转载:https://baorongquan.github.io/2018/05/06/%E8%AE%B0%E4%B8%80%E6%AC%A1golang%E7%BB%8F%E5%85%B8%E9%94%99%E8%AF%AF-for%E5%BE%AA%E7%8E%AF%E4%B8%AD%E7%9A%84go%E5%8D%8F%E7%A8%8B%E8%B0%83%E7%94%A8/

产生上述问题的本质是,golang的for循环会使用同一个变量来存储迭代过程中的临时变量,在将该变量传递给goroutine时,goroutine得到的是该变量的地址,又由于goroutine的启动与调度机制有关,可能for循环执行完后,goroutine才开始调度,所以导致多个goroutine访问的是同一个数据。

原文地址:https://www.cnblogs.com/leadership/p/12090849.html

时间: 2024-08-29 13:06:37

Golang for 循环中使用goroutine的相关文章

Golang网络库中socket阻塞调度源码剖析

本文分析了Golang的socket文件描述符和goroutine阻塞调度的原理.代码中大部分是Go代码,小部分是汇编代码.完整理解本文需要Go语言知识,并且用Golang写过网络程序.更重要的是,需要提前理解goroutine的调度原理. 1. TCP的连接对象: 连接对象: 在net.go中有一个名为Conn的接口,提供了对于连接的读写和其他操作: type Conn interface { Read(b []byte) (n int, err error) Write(b []byte)

退出循环break,在while、for、do...while、循环中使用break语句退出当前循环,直接执行后面的代码。

在while.for.do...while循环中使用break语句退出当前循环,直接执行后面的代码. 格式如下: for(初始条件;判断条件;循环后条件值更新) { if(特殊情况) {break;} 循环代码 } 当遇到特殊情况的时候,循环就会立即结束.看看下面的例子,输出10个数,如果数值为5,就停止输出.

python 如何在一个for循环中遍历两个列表

是我在看<笨方法学python>过程中发现有一行代码看不懂——“ for sentence in snippet, phrase:”,所以研究了半天,感觉挺有收获的.所以就放在博客上分享给大家了. 直入主题: 为了不耽误大家时间,如果知道以下为两段代码为什么输出不一样的话我觉得您肯定知道我下面要说的是什么了,您就不必花时间再读下去了. 1和2两段代码的区别是print在for循环中,另外一个是不在循环中.输出的结果却截然不同,如果想要弄懂如何遍历两列表,花些时间把下面的几行代码弄懂.我要说的您

Handlebars.js循环中索引(@index)使用技巧(访问父级索引)

使用Handlebars.js过程中,难免会使用循环,比如构造数据表格.而使用循环,又经常会用到索引,也就是获取当前循环到第几次了,一般会以这个为序号显示在页面上. Handlebars.js中获取循环索引很简单,只需在循环中使用{{@index}}即可. 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-

golang解析mongodb中的ISODate类型

在golang中可以使用time.Time数据类型来保存mongodb中的ISODate时间. g type Model struct {     uploadDate time.Time `bson:"uploadDate"` } m := Model{} if err := c.Find(nil).Select({"_id": 0, "uploadDate": 1}).One(&m); err != nil {     fmt.Pri

关于在for循环中绑定事件打印变量i是最后一次。

其实函数引用的外部变量都是最后一次的值. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #box{ width:100px; height:100px; background-color:pink; } </style> <

matlab循环中显示figure时窗口跳动

在Matlab中,当在一个循环内部利用figure显示图片时,有时候会出现窗口跳动,尤其是两个显示器的时候, 具体就是每次循环中显示的figure的位置都出现在屏幕的不同位置,导致看起来灰常不爽 google了个解决方法: 利用imshow中的parent参数,将axis设置为与上一状态一致. code: figure(1);imshow(img,[],'parent',gca);

bash的循环中无法保存变量

在bash中,如果循环在一个子shell中运行,那么在循环中对循环外面的变量的更改将在循环退出后不可见.像下面的例子: #!/bin/sh var=1 python run.py | while read line do echo $line var=var+1 let "var=var+1" done echo $var 最后面的echo输出的var仍然是1.原因是while循环位于管道中,这意味着在运行过程中,while循环实际是位于一个新的SHELL中的,while循环中的var

循环中不要放入openSession()

for(Shop s:list) { System.out.println(s.getName()); String sql="select shopId,sum(ele_bank+ele_card+water_card+water_bank+coal_card+coal_bank) as zonghe ,sum(sale_bank+sale_card) as sale,sum(cigarette_card+cigarette_bank) as bacco, sum(yidong_card+yi