为了账号安全,请及时绑定邮箱和手机立即绑定

如何从goroutine的channel中持续接收数据

如何从goroutine的channel中持续接收数据

Go
当年话下 2021-11-29 15:49:04
我是 Golang 的初学者。我已经做了一个关于 Go 频道的练习。我打开并从主 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 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

Method2 和 Method3 弄乱了文本,因为在读取器和写入器共享的缓冲区上存在竞争。


以下是上述程序可能的语句执行顺序:


 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

使用种族检测器运行您的程序。它将为您标记问题。


解决问题的一种方法是复制数据。例如,从读取的字节创建一个字符串并将该字符串发送到通道。


另一种选择是将 goroutine 与io.Pipe连接。一个 goroutine 从源读取并写入管道。另一个 goroutine 从管道读取并写入目标。管道负责同步问题。


查看完整回答
反对 回复 2021-11-29
?
慕容3067478

TA贡献1773条经验 获得超3个赞

为了使用您在注释中作为Method2and放置的 for 循环获取代码片段Method3,您将需要使用缓冲通道。


文本在目标文件中混乱的原因是循环 infunc main没有机制与在WriteFile.


另一方面,发送到缓冲通道,仅在缓冲区已满时阻塞。当缓冲区为空时接收块。因此,通过初始化缓冲区长度为 1 的通道,您可以使用Method1和/或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)

    }

}


查看完整回答
反对 回复 2021-11-29
  • 2 回答
  • 0 关注
  • 159 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信