GO for range channel

972 阅读1分钟

这周在开发项目过程中遇到了一点问题,代码示例如下:

package main

import (
	"fmt"
	"strconv"
	"time"
)

var (
	verchan   chan string
	maxLength = 3
)

func main() {
	verchan = make(chan string, 50)
	for i := 0; i < 4; i++ {
		verchan <- strconv.Itoa(i)
	}
	go listener()
	time.Sleep(500000000000)
}

func listener() {
	var buffer []string
	startTime := time.Now().Unix()

	for {
		for chanVal := range verchan {
			buffer = append(buffer, chanVal)
			if len(buffer) == maxLength { // buffer length 达到 maxLength 执行一次批量操作
				go func(buffer []string) {
                                        // do something
					fmt.Println(buffer, "go buffer")
					startTime = time.Now().Unix()
				}(buffer)
				buffer = make([]string, 0) //
				fmt.Println(buffer, "buffer")
			}
		}
		endTime := time.Now().Unix()
		timeSpace := endTime - startTime
		fmt.Println(timeSpace, "timeSpace")
		if timeSpace > 2 {
			// do something
			startTime = time.Now().Unix()
		}

		time.Sleep(time.Millisecond * 500)
	}
}

这段代码的本意是想做一个批量的数据入库操作,每500毫秒从channel中取出数据 当 buffer 的长度达到设置的 maxLength 时,执行一次批量操作,或者超过 2s 没有数据写入channel 中直接将 buffer 的数据进行一次批量操作。

但程序运行后发现并未达到我们的预期 结果如下:

[] buffer
[] go buffer

究其原因很简单,忽略了for range channel的一个特性,不同于for i :=0; i<len; i++ 这种循环(这种固定了在channel中取len条数据),for range channel 会一直遍历等待通道 close 知道原因修改就简单了:

       for {
		verlen := len(verchan)
		for i := 0; i < verlen; i++ {
			buffer = append(buffer, <-verchan)
			if len(buffer) == maxLength {
				go func(buffer []string) {
				}(buffer)
				buffer = []string{}
				fmt.Println(buffer, "bufferDEL")
			}
		}
                ...
	}

这样的方式就能到达想要的效果了