【记录一个伟大的坑】for 循环写进chan局部变量指针

敲代码三年了,刚从Java开始,我就一直秉持着当使用循环时我尽量把临时的局部变量定义再循环体外,因为这样可以节约内存的开销,毕竟没声明一个变量,系统都要给他分配地址,都要消耗内存,请看下面代码:

	for i := 0; i < 100; i++ {
		var tmp = strconv.Itoa(i)
		fmt.Println(&tmp,":",tmp)
	}

	fmt.Println("*****************************************")
	var tmp string
	for i := 0; i < 100; i++ {
		tmp = strconv.Itoa(i)
		fmt.Println(&tmp,":",tmp)
	}

打印:

在这点代码里,上面的for循环会为tmp内存开辟一百个地址!而下面的始终只分配了一个地址,变化的只是值。

为了代码的质量,我一直把变量的定义放在循环体外。直到今天,直到刚刚,昨晚我用go去读取一个本地的大数据文本,是一个1.5G的txt文件,我要把里面的数据读取到MySQL中去,昨天就写好了,可是执行后我发现总是有重复的写入!我又把业务逻辑梳理了一遍,一遍又一遍,从打开数据库建立连接,打开大数据文本,bufio的读取,数据的截取,转换go数据,写入通道(因为数据量庞大,我开了1000的并发协程,把通道的容量设定为10e6)……一遍又一遍的把业务逻辑梳理了,最后我坚信逻辑是没错的,我又开始从方法上去找问题,一个一个的打印排除,凌晨一点多,我搞不下去了,我并没有像上周搞到凌晨6点,我清楚这时候需要放下,去睡一觉,我可能走入了某个误区。

上午,又树立了一遍,用倒退的方式排查,读取的chan通道里面又重复的数据,也就更加验证了不是并发协程的问题,那就是写入通道时,bufio的readString和readLine是经常用到的,这里我也就换了一下,再往下,我是把读到的内容整理封装成一个结构体也就是Java的类,然后把结构体的指针放进通道的!就这里了,我把结构体的定义初始化放在了循环体的外部,就是我的这一习惯,每次循环放进管道的都是同一个地址!!!我管道里原来放了10000000个一成不变的地址!快被自己笨哭了!一个负责读去文本的协程负责想同一个地址里面写数据却要为10000个协程服务,(为了进一步提高效率,我又把并发开到了10000)而这10000的并发却都获取同一个地址的内容往数据中执行写入!只有当写入的结构体的协程完成了,后面的读取机构提的值才会变,然而就在这一瞬间,有N多的协程并发的读取了管道的地址的值,并执行了写入!造成了重复!

代码改为初始化放进了循环体内部,每次循环都丢进管道的都是一个新的地址!解决问题!

问题代码块如下!

	reader := bufio.NewReader(file)
	//kfps := new(kfperson)   ///这里曾经埋葬了我一夜!!!!!!
	for {
		linStr, e := reader.ReadString('\n')
		if e == io.EOF {
			close(chbd)
			close(chkp)
			break
		}
		HandleError(e, "reader.ReadString")
		linSplic := strings.Split(linStr, ",")

		kfps := new(kfperson)
		kfps.Name, kfps.Idcard = linSplic[0], linSplic[1]

		chkp <- kfps
	}

猜你喜欢

转载自blog.csdn.net/ckx178/article/details/88714524
今日推荐