简体   繁体   English

如何从goroutine的频道连续接收数据

[英]How to continuously receive data from channel of goroutine

I am a beginner of the Golang. 我是Golang的初学者。 I had made a practice about Go channel. 我曾经做过关于Go频道的练习。 Which I open and read data from a file in the main goroutine, then pass the data to the second goroutine to save to another file with channel. 我打开并从主goroutine中的文件读取数据,然后将数据传递到第二个goroutine以保存到具有通道的另一个文件。 My code is as flows 我的代码是流程

  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()
}

My question is why the Method2 and Method3 will get messed text in the target file? 我的问题是为什么Method2和Method3会在目标文件中得到混乱的文本? How can I fix it? 我该如何解决?

Method2 and Method3 get messed up text because there's a race on the buffer shared by the reader and writer. 方法2和方法3搞乱了文本,因为读写器共享的缓冲区上存在争用。

Here's a possible sequence of statement execution for the program above: 以下是上述程序可能的语句执行顺序:

 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 your program with the race dectector . 使用race dectector运行程序。 It will flag the issues for you. 它会为您标记问题。

One way to fix the problem is to copy the data. 解决问题的一种方法是复制数据。 For example, create a string from the bytes read and send the string to the channel. 例如,从读取的字节创建一个字符串,并将该字符串发送到该通道。

Another option is to connect the goroutines with an io.Pipe . 另一种选择是将goroutines与io.Pipe连接起来 One goroutine reads from the source and writes to the pipe. 一个goroutine从源读取并写入管道。 The other goroutine reads from the pipe and writes to the destination. 另一个goroutine从管道读取并写入目标。 The pipe takes care of the synchronization issues. 管道处理同步问题。

In order to get the code snippets using the for loops in what you have put in comments as Method2 and Method3 , you will need to use a buffered channel. 为了获得在你提出什么意见作为使用for循环的代码片段Method2Method3 ,您需要使用一个缓冲通道。

The reason the text gets messed up in the target file is the loop in func main has no mechanism to synchronize in lock step with the loops listening on the channel in WriteFile . 文本在目标文件中混乱的原因是func main的循环没有机制在锁定步骤中与在WriteFile监听通道的循环同步。

Sends to a buffered channel, on the other hand, block only when the buffer is full. 另一方面,发送到缓冲通道仅在缓冲区已满时阻止。 Receives block when the buffer is empty. 缓冲区为空时接收阻止。 So by initializing a channel with a buffer length of one, your can use Method1 and/or Method2 . 因此,通过与一个的缓冲长度初始化的频道,可以使用Method1和/或Method2 All that's left is remembering to close the channel when you are done. 剩下的就是记住在完成后关闭频道。

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)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM