猿问

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

我是 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 会在目标文件中弄乱文本?我该如何解决?


当年话下
浏览 160回答 2
2回答

扬帆大鱼

Method2 和 Method3 弄乱了文本,因为在读取器和写入器共享的缓冲区上存在竞争。以下是上述程序可能的语句执行顺序:&nbsp;R: bytes_len, err = f.Read(buf)&nbsp;&nbsp;&nbsp;R: ch<-buf[:bytes_len]&nbsp;W: bytes, ok := <-ch; ok&nbsp;R: bytes_len, err = f.Read(buf)&nbsp; // this writes over buffer&nbsp;W: f.Write(bytes)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // writes data from second read使用种族检测器运行您的程序。它将为您标记问题。解决问题的一种方法是复制数据。例如,从读取的字节创建一个字符串并将该字符串发送到通道。另一种选择是将 goroutine 与io.Pipe连接。一个 goroutine 从源读取并写入管道。另一个 goroutine 从管道读取并写入目标。管道负责同步问题。

慕容3067478

为了使用您在注释中作为Method2and放置的 for 循环获取代码片段Method3,您将需要使用缓冲通道。文本在目标文件中混乱的原因是循环 infunc main没有机制与在WriteFile.另一方面,发送到缓冲通道,仅在缓冲区已满时阻塞。当缓冲区为空时接收块。因此,通过初始化缓冲区长度为 1 的通道,您可以使用Method1和/或Method2. 剩下的就是记住在完成后关闭通道。func main() {&nbsp; &nbsp; f, _ := os.OpenFile("test.txt", os.O_RDONLY, 0600)&nbsp; &nbsp; defer f.Close()&nbsp; &nbsp; ch := make(chan []byte, 1) // use second argument to make to give buffer length 1&nbsp; &nbsp; buf := make([]byte, 10)&nbsp; &nbsp; go WriteFile(ch)&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; ch <- buf&nbsp; &nbsp; &nbsp; &nbsp; byteLen, err := f.Read(buf)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if byteLen < 10 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch <- buf[:byteLen]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; close(ch) //close the channel when you done}func WriteFile(ch <-chan []byte) {&nbsp; &nbsp; f, err := os.OpenFile("othertest.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)&nbsp; &nbsp; defer f.Close()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("* Error:", err)&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; //Method 3: use "for range"&nbsp; &nbsp; for data := range ch {&nbsp; &nbsp; &nbsp; &nbsp; f.Write(data)&nbsp; &nbsp; }}
随时随地看视频慕课网APP

相关分类

Go
我要回答