【问题标题】:How to continuously receive data from channel of goroutine如何从goroutine的通道连续接收数据
【发布时间】:2016-03-16 21:13:30
【问题描述】:

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

【问题讨论】:

    标签: go channel


    【解决方案1】:

    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
    

    运行你的程序with the race dectector。它会为您标记问题。

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

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

    【讨论】:

    • 是的,Slice 是一个引用类型,所以读写会竞争。当使用像“字符串”这样的值类型时,效果很好。
    • 而且,当我使用sync.Mutex来同步这两个goroutine中的读写操作时,程序总是会陷入死锁。
    【解决方案2】:

    为了使用您在 cmets 中作为 Method2Method3 放置的 for 循环来获取代码 sn-ps,您需要使用 buffered 通道。

    文本在目标文件中被弄乱的原因是func 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)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-04
      • 1970-01-01
      • 1970-01-01
      • 2014-10-10
      • 2021-12-05
      • 1970-01-01
      • 2017-09-20
      相关资源
      最近更新 更多