Golang中国

请高手看看是否是Go语言的bug

在使用引用类型(如[]byte)的chan时,会有冲突

byteChan := make([]byte)

对于这个chan bytechan,部分代码如下:

go func f1() {
    buf := make([]byte, 100)
    for i := 0; i <1000; i++ {
        str := "line " + string(i)
        buf = []byte(str)
        byteChan <- buf
    }
}()

go func f2() {
    for c := range byteChan {
        fmt.Println("received:", string(v))
    }
}()

期望的结果是输出:
line 0
line 1

line 999

但实际上很可能会输出:
line 0
line 2
line 2
line 4

上面的例子只是为了说明问题。实际应用中我是一个goroutine读取文件,通过[]byte chan传递,另一个goroutine接受并处理。结果发现丢失数据。后来我修改了一下代码,问题消失。我的初步结论是:引用类型的chan,传递的是变量的地址,这导致接收方正在使用该变量时,发送方同时修改了变量值。

我的代码修改如下(上面的例子)

go func f1() {
    buf := make([]byte, 100)
    for i := 0; i <1000; i++ {
        str := "line " + string(i)
        buf = []byte(str)
        byteChan <- buf
        buf = make([]bye, 100)
    }
}()

也就是发送变量到chan之后,要重新构造一个引用类型的变量。

请高手指教。

7 回复
NEO
#1 NEO • 2015-11-21 09:18

文中有错误 byteChan := make([]byte)应该为byteChan := make(chan []byte)

joe9i0
#2 joe9i0 • 2015-11-21 10:38

楼主你是手写的代码吗? 错误百出…

package main

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



func main(){
    byteChan := make(chan []byte)

    go func() {
        for i := 0; i <1000; i++ {
            str := "line " + strconv.Itoa(i)
            byteChan <- []byte(str)
        }
    }()

    go func() {
        for c := range byteChan {
            fmt.Println("received:", string(c))
        }
    }()

    time.Sleep(3*time.Second)
}
NEO
#3 NEO • 2015-11-21 11:07

是在帖子里临时写的代码,只是为了说明问题,有错误,不好意思。Go还不熟悉,见谅。我的问题你遇到了吗?

stevewang
#4 stevewang • 2015-11-21 18:13

不同的goroutine修改同一个[]byte需要做同步处理。

laoshe
#5 laoshe • 2015-11-22 11:31

楼主的代码根本不能描述问题.
出现你那种错误只有可能是多个Goroutine同时使用buf.你的buf肯定放在Goroutine外面定义声明的 ,像是这样

//定义在外面
var buf = make([]byte, 100)

    for ii := 0; ii < 2; ii++ {
        go func(i int) {
            for i := 0; i < 100; i++ {
                str := strconv.Itoa(ii) + " line " + strconv.Itoa(i)
                buf = []byte(str)
                byteChan <- buf
            }
        }(ii)
    }

而不是你描述代码里放在每个Goroutine里面

go func f1() {
    //在Goroutine里面声明定义,只有这个Goroutine使用
    buf := make([]byte, 100)

    for i := 0; i <1000; i++ {
        str := "line " + string(i)
        buf = []byte(str)
        byteChan <- buf
    }
}()
NEO
#6 NEO • 2015-11-23 08:18

放在里面就没问题了吗?你测试过吗?我回头测试一下。

我刚刚查了代码,是在goroutine里面声明的buf,不存在共享可能

bigbear
#7 bigbear • 2015-11-23 09:02

str := “line “ + strconv.Itoa(i)

需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。