新手学习 go ,从通道中获取数据为什么顺序是错乱的,下面是我写的伪代码
package main
import (
"fmt"
"io"
"net/http"
"os"
"sync"
)
var wg sync.WaitGroup
func Gets(uri string, buf chan []byte) {
resp, err := http.Get(uri)
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
bufs := make([]byte, 128)
for {
n, err1 := resp.Body.Read(bufs[:])
if err1 == io.EOF {
break
}
buf <- bufs[:n]
fmt.Println(string(bufs[:n]))
}
fmt.Println("读取数据完毕")
close(buf)
}
func SaveFile(name string, buf chan []byte) {
defer wg.Done()
f, err2 := os.Create(name)
if err2 != nil {
return
}
defer f.Close()
for {
if data, ok := <-buf; ok {
f.Write(data)
} else {
break
}
}
f.Sync()
fmt.Println("写入数据完毕")
}
func main() {
for i := 1; i < 2; i++ {
wg.Add(1)
buf := make(chan []byte)
files1 := "test1.txt"
urils := "http://www.baidu.cn"
go SaveFile(files1, buf)
go Gets(urils, buf)
}
wg.Wait()
}
fmt.Println(string(bufs[:n]))这个和 test1.txt 中内容不一致啊,这个是为什么,各位大神帮忙分析下。
1
yunshangdetianya OP 如果把通道改为带缓冲的,那么顺序就更乱了
``` buf := make(chan []byte,128) ``` |
2
useben 2022-08-08 18:11:20 +08:00
for i := 1; i < 2; i++ {
wg.Add(1) buf := make(chan []byte) files1 := "test1.txt" urils := "http://www.baidu.cn" go SaveFile(files1, buf) go Gets(urils, buf) } 分别创建 2 个 SaveFile/Gets 的 goroutine 了.... |
3
hsfzxjy 2022-08-08 18:16:43 +08:00
因为你多次<-使用的是同一个 buffer ,可能 SaveFile 里 f.Write 还没执行完,Gets 里 resp.Body.Read 就把上一次的内容覆盖了
|
4
keepeye 2022-08-08 18:22:25 +08:00
你分别创建了两个读 两个写的线程,并行的,肯定乱了
|
5
keepeye 2022-08-08 18:22:56 +08:00
不要把 go 当作 await 用
|
6
yunshangdetianya OP @useben 让我好好想想,感觉没转过来
|
7
yunshangdetianya OP @keepeye 我好像还没反应过来
|
8
yunshangdetianya OP @keepeye 那个 for 循环不是只执行一次吗? for i := 1; i < 2; i++
|
9
yunshangdetianya OP @keepeye 为何会创建 2 个呢?
|
10
keepeye 2022-08-09 09:45:16 +08:00
抱歉,看错了,重新过了一遍代码,问题可能出在 buf <- bufs[:n] 这句上,切片是引用,你最好 copy 到一个新的 slice 上:
newBuf := make([]byte, n) copy(newBuf, bufs[:n]) buf <- newBuf 试一下看看 |
11
yunshangdetianya OP @keepeye 好的,我去试下,非常感谢
|
12
yunshangdetianya OP @keepeye 的确是可以了,正常了,但是为何拷贝过去就正常了呢?
|
13
keepeye 2022-08-09 10:15:32 +08:00
因为切片的切片还是指向同一个底层数组,在 write 的时候底层数组也同时被 read 操作覆盖了。
举个例子: a := []int{1,2,3} b := a[:1] a[0] = 4 fmt.Println(b) // [4] 具体可以阅读这篇文章: https://go.dev/blog/slices-intro |
14
yunshangdetianya OP @keepeye 非常感谢,我去看下文章
|