如何从goroutine的channel持续接收数据
How to continuously receive data from channel of goroutine
我是Golang的初学者。我做了一个关于 Go channel 的练习。我打开并从主 goroutine 中的文件读取数据,然后将数据传递给第二个 goroutine 以保存到另一个带有通道的文件。
我的代码很流畅
func main() {
f, err := os.OpenFile("test.go", os.O_RDONLY, 0600)
ch := make(chan []byte)
buf := make([]byte, 10)
bytes_len, err := f.Read(buf)
fmt.Println("ReadLen:", bytes_len)
if err != nil {
fmt.Println("Error: ", err)
return
}
go WriteFile(ch)
for {
ch<-buf
bytes_len, err = f.Read(buf)
if err != nil {
fmt.Println("error=", err)
break
}
if bytes_len < 10 {
ch<-buf[:bytes_len]
fmt.Println("Finished!")
break
}
}
time.Sleep(1e9)
f.Close()
}
func WriteFile(ch <-chan []byte) {
fmt.Println("* begin!")
f, err := os.OpenFile("/home/GoProgram/test/test.file", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
if err != nil {
fmt.Println("* Error:", err)
return
}
/* Method1: use the "select" will write to target file OK, but it is too slow!!!
for {
select {
case bytes, ok:= <-ch:
if ok {
f.Write(bytes)
} else {
fmt.Println("* file closed!")
break
}
default:
fmt.Println("* waiting data!")
}
} \*/
// Method 2: use "for {if}", this will get messed text in target file, not identical with the source file.
for {
if bytes, ok := <-ch; ok {
f.Write(bytes)
fmt.Println("* buff=", string(bytes))
bytes = nil
ok = false
} else {
fmt.Println("** End ", string(bytes), " ", ok)
break
}
}
/* Method 3: use "for range", this will get messed text like in method2
for data:= range ch {
f.Write(data)
//fmt.Println("* Data:", string(data))
}
\*/
f.Close()
}
我的问题是为什么Method2和Method3会在目标文件中得到乱码?我该如何解决?
方法 2 和方法 3 弄乱了文本,因为在 reader 和作者共享的缓冲区上存在争用。
下面是上述程序可能的语句执行顺序:
R: bytes_len, err = f.Read(buf)
R: ch<-buf[:bytes_len]
W: bytes, ok := <-ch; ok
R: bytes_len, err = f.Read(buf) // this writes over buffer
W: f.Write(bytes) // writes data from second read
运行 你的程序 with the race dectector。它会为您标记问题。
解决该问题的一种方法是复制数据。例如,从读取的字节创建一个字符串并将该字符串发送到通道。
另一种选择是将 goroutines 与 io.Pipe 连接起来。一个 goroutine 从源读取并写入管道。另一个 goroutine 从管道读取并写入目的地。管道负责同步问题。
为了在您作为 Method2
和 Method3
的注释中使用 for 循环获取代码片段,您需要使用 buffered 频道。
文本在目标文件中混乱的原因是 func main
中的循环没有机制来同步锁步与在 WriteFile
.[=18 中监听通道的循环=]
发送到缓冲通道,另一方面,仅当缓冲区已满时才阻塞。当缓冲区为空时接收块。因此,通过初始化缓冲区长度为 1 的通道,您可以使用 Method1
and/or Method2
。剩下的就是记得在完成后关闭频道。
func main() {
f, _ := os.OpenFile("test.txt", os.O_RDONLY, 0600)
defer f.Close()
ch := make(chan []byte, 1) // use second argument to make to give buffer length 1
buf := make([]byte, 10)
go WriteFile(ch)
for {
ch <- buf
byteLen, err := f.Read(buf)
if err != nil {
break
}
if byteLen < 10 {
ch <- buf[:byteLen]
break
}
}
close(ch) //close the channel when you done
}
func WriteFile(ch <-chan []byte) {
f, err := os.OpenFile("othertest.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
defer f.Close()
if err != nil {
fmt.Println("* Error:", err)
return
}
//Method 3: use "for range"
for data := range ch {
f.Write(data)
}
}
我是Golang的初学者。我做了一个关于 Go channel 的练习。我打开并从主 goroutine 中的文件读取数据,然后将数据传递给第二个 goroutine 以保存到另一个带有通道的文件。 我的代码很流畅
func main() {
f, err := os.OpenFile("test.go", os.O_RDONLY, 0600)
ch := make(chan []byte)
buf := make([]byte, 10)
bytes_len, err := f.Read(buf)
fmt.Println("ReadLen:", bytes_len)
if err != nil {
fmt.Println("Error: ", err)
return
}
go WriteFile(ch)
for {
ch<-buf
bytes_len, err = f.Read(buf)
if err != nil {
fmt.Println("error=", err)
break
}
if bytes_len < 10 {
ch<-buf[:bytes_len]
fmt.Println("Finished!")
break
}
}
time.Sleep(1e9)
f.Close()
}
func WriteFile(ch <-chan []byte) {
fmt.Println("* begin!")
f, err := os.OpenFile("/home/GoProgram/test/test.file", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
if err != nil {
fmt.Println("* Error:", err)
return
}
/* Method1: use the "select" will write to target file OK, but it is too slow!!!
for {
select {
case bytes, ok:= <-ch:
if ok {
f.Write(bytes)
} else {
fmt.Println("* file closed!")
break
}
default:
fmt.Println("* waiting data!")
}
} \*/
// Method 2: use "for {if}", this will get messed text in target file, not identical with the source file.
for {
if bytes, ok := <-ch; ok {
f.Write(bytes)
fmt.Println("* buff=", string(bytes))
bytes = nil
ok = false
} else {
fmt.Println("** End ", string(bytes), " ", ok)
break
}
}
/* Method 3: use "for range", this will get messed text like in method2
for data:= range ch {
f.Write(data)
//fmt.Println("* Data:", string(data))
}
\*/
f.Close()
}
我的问题是为什么Method2和Method3会在目标文件中得到乱码?我该如何解决?
方法 2 和方法 3 弄乱了文本,因为在 reader 和作者共享的缓冲区上存在争用。
下面是上述程序可能的语句执行顺序:
R: bytes_len, err = f.Read(buf)
R: ch<-buf[:bytes_len]
W: bytes, ok := <-ch; ok
R: bytes_len, err = f.Read(buf) // this writes over buffer
W: f.Write(bytes) // writes data from second read
运行 你的程序 with the race dectector。它会为您标记问题。
解决该问题的一种方法是复制数据。例如,从读取的字节创建一个字符串并将该字符串发送到通道。
另一种选择是将 goroutines 与 io.Pipe 连接起来。一个 goroutine 从源读取并写入管道。另一个 goroutine 从管道读取并写入目的地。管道负责同步问题。
为了在您作为 Method2
和 Method3
的注释中使用 for 循环获取代码片段,您需要使用 buffered 频道。
文本在目标文件中混乱的原因是 func main
中的循环没有机制来同步锁步与在 WriteFile
.[=18 中监听通道的循环=]
发送到缓冲通道,另一方面,仅当缓冲区已满时才阻塞。当缓冲区为空时接收块。因此,通过初始化缓冲区长度为 1 的通道,您可以使用 Method1
and/or Method2
。剩下的就是记得在完成后关闭频道。
func main() {
f, _ := os.OpenFile("test.txt", os.O_RDONLY, 0600)
defer f.Close()
ch := make(chan []byte, 1) // use second argument to make to give buffer length 1
buf := make([]byte, 10)
go WriteFile(ch)
for {
ch <- buf
byteLen, err := f.Read(buf)
if err != nil {
break
}
if byteLen < 10 {
ch <- buf[:byteLen]
break
}
}
close(ch) //close the channel when you done
}
func WriteFile(ch <-chan []byte) {
f, err := os.OpenFile("othertest.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
defer f.Close()
if err != nil {
fmt.Println("* Error:", err)
return
}
//Method 3: use "for range"
for data := range ch {
f.Write(data)
}
}