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

Hen*_*ohn 5 channel go

我是 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()
}
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么 Method2 和 Method3 会在目标文件中得到混乱的文本?我该如何修复它?

Cer*_*món 4

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
Run Code Online (Sandbox Code Playgroud)

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

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

另一种选择是使用io.Pipe连接 goroutine 。一个 goroutine 从源中读取数据并将其写入管道。另一个 goroutine 从管道中读取数据并将其写入目的地。管道负责处理同步问题。