簡體   English   中英

go中如何使用channel?

[英]How to use channels in go?

我有一個函數,它接受一個 int 數組並將它們轉儲到一個通道中。

func Dump(a []int, ch chan int) {
    for i := range a {
        ch <- i
    }
    close(ch)
}

這個主要不建立:

func main() {
    ch := make(chan int)
    arr := []int{1, 2, 3, 4, 5}
    Dump(arr, ch)
    for i := range ch {
        fmt.Printf("Got %v\n", i)
    }
}

拋出這個錯誤:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.Dump(0xc000078f48, 0x5, 0x5, 0xc00006e060)
        /Users/300041738/go-workspace/src/test.go:7 +0x43
main.main()
        /Users/300041738/go-workspace/src/test.go:15 +0x9b
exit status 2

但是,這會構建:

func main() {
    ch := make(chan int)
    arr := []int{1, 2, 3, 4, 5}
    go Dump(arr, ch)
    for i := range ch {
        fmt.Printf("Got %v\n", i)
    }
}

為什么我必須在 Dump 前面寫 go? 我不想異步轉儲數組的內容。

通道有緩沖區。 默認情況下,緩沖區的大小為 0。換句話說,如果一個元素要插入到一個非緩沖的通道中,插入的 goroutine 將停止,直到另一個 goroutine 從通道中檢索到值。

所以為了好玩,試試這個:

import "fmt"

func Dump(a []int, ch chan int) {
    for i := range a {
        ch <- i
    }
    close(ch)
}

func main() {
    arr := []int{1, 2, 3, 4, 5}
    ch := make(chan int, len(arr)) //specify channel buffer length equal to arr size
    Dump(arr, ch)
    for {
        i, ok := <- ch
        if ok {
            fmt.Println("received a number !", i)
        } else {
            fmt.Println("channel is closed, we're done here")
        }
    }
}

解除鎖定是因為您的主要 goroutine 正在嘗試寫入通道,但沒有人從中讀取。 您不需要在此處使用緩沖通道。

go中有流水線的概念。 您的Dump函數基本上就像這里的管道源。 您可以將Dump函數修改為如下所示:

func Dump(a []int) chan int {
  ch := make(chan int)
  go func() {
    for i := range a {
      ch <- i
    }
    close(ch)
  }()
  return ch
}

請注意,我現在正在一個單獨的 go 例程中寫入通道。

問題的根源 - 當您寫入無緩沖通道時,它會被鎖定,直到有人從通道中讀取值。 對於無緩沖通道 - 在每次寫入操作后,您必須執行一次讀取操作。 否則你的第二個寫操作將被鎖定,直到有人讀取第一個值。 在你的第一個例子中,它永遠不會發生。

為了顯示:

  1. 調用Dump()函數
  2. 運行for循環的第一次迭代
  3. i值寫入通道
  4. 運行for循環的第二次迭代
  5. 嘗試將下一個i值寫入通道,但它鎖定,沒有人讀取第一個值。
  6. 由於所有工作都在單個主 goroutine 中完成,因此整個應用程序都被鎖定。 結果陷入僵局。

添加 goroutine 用法時會發生這種情況( go Dump(arr, ch) ):

  1. 在單獨的 goroutine 中調用Dump()函數
  2. 運行for循環的第一次迭代
  3. i值寫入通道
  4. 運行for循環的第二次迭代
  5. 嘗試將下一個i值寫入通道,但它鎖定,沒有人讀取第一個值。
  6. Goroutine 被鎖定(我們運行Dump() ),但我們有主 goroutine 和 go scheduler 將控制切換到主 goroutine。
  7. 這意味着該行實際上是for i := range ch 它終於讀取了第一個值!
  8. 現在Dump() goroutine 已解鎖,可以寫入第二個值。
  9. 控制將在兩個 goroutine 之間傳遞,直到所有工作完成。

請注意,執行的確切順序可能會有所不同(基於 go 調度程序的邏輯)。 要使用它,您可以使用go Dump(arr, ch)將 print 添加到Dump()函數中:

func Dump(a []int, ch chan int) {
    for i := range a {
        fmt.Printf("Write %v\n", i)
        ch <- i
    }
    close(ch)
}

您將看到 Write 和 Got 消息會混合在一起。

很難為您的答案提供解決方案,因為它是一個沙箱示例,實際上不需要該通道。

通過使用緩沖通道,假設大小為n ,您可以在不鎖定和讀取的情況下進行n寫入。 我建議您查看Go 頻道基礎知識

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM