V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yunshangdetianya
V2EX  ›  Go 编程语言

为何从通道取数据顺序是错误的?

  •  
  •   yunshangdetianya · 2022-08-08 17:29:31 +08:00 · 1487 次点击
    这是一个创建于 839 天前的主题,其中的信息可能已经有所发展或是发生改变。

    新手学习 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 中内容不一致啊,这个是为什么,各位大神帮忙分析下。

    14 条回复    2022-08-09 10:23:49 +08:00
    yunshangdetianya
        1
    yunshangdetianya  
    OP
       2022-08-08 17:38:39 +08:00
    如果把通道改为带缓冲的,那么顺序就更乱了
    ```
    buf := make(chan []byte,128)
    ```
    useben
        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 了....
    hsfzxjy
        3
    hsfzxjy  
       2022-08-08 18:16:43 +08:00
    因为你多次<-使用的是同一个 buffer ,可能 SaveFile 里 f.Write 还没执行完,Gets 里 resp.Body.Read 就把上一次的内容覆盖了
    keepeye
        4
    keepeye  
       2022-08-08 18:22:25 +08:00
    你分别创建了两个读 两个写的线程,并行的,肯定乱了
    keepeye
        5
    keepeye  
       2022-08-08 18:22:56 +08:00
    不要把 go 当作 await 用
    yunshangdetianya
        6
    yunshangdetianya  
    OP
       2022-08-09 09:34:54 +08:00
    @useben 让我好好想想,感觉没转过来
    yunshangdetianya
        7
    yunshangdetianya  
    OP
       2022-08-09 09:35:25 +08:00
    @keepeye 我好像还没反应过来
    yunshangdetianya
        8
    yunshangdetianya  
    OP
       2022-08-09 09:37:08 +08:00
    @keepeye 那个 for 循环不是只执行一次吗? for i := 1; i < 2; i++
    yunshangdetianya
        9
    yunshangdetianya  
    OP
       2022-08-09 09:37:32 +08:00
    @keepeye 为何会创建 2 个呢?
    keepeye
        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

    试一下看看
    yunshangdetianya
        11
    yunshangdetianya  
    OP
       2022-08-09 09:56:24 +08:00
    @keepeye 好的,我去试下,非常感谢
    yunshangdetianya
        12
    yunshangdetianya  
    OP
       2022-08-09 09:59:53 +08:00
    @keepeye 的确是可以了,正常了,但是为何拷贝过去就正常了呢?
    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
    yunshangdetianya
        14
    yunshangdetianya  
    OP
       2022-08-09 10:23:49 +08:00
    @keepeye 非常感谢,我去看下文章
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2815 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:46 · PVG 19:46 · LAX 03:46 · JFK 06:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.