📜  Golang 中的频道

📅  最后修改于: 2021-10-24 14:17:39             🧑  作者: Mango

在 Go 语言中,通道是一个 goroutine 与另一个 goroutine 通信的媒介,并且这种通信是无锁的。或者换句话说,通道是一种允许让一个 goroutine 向另一个 goroutine 发送数据的技术。默认情况下通道是双向的,这意味着 goroutine 可以通过相同的通道发送或接收数据,如下图所示:

创建频道

在 Go 语言中,通道是使用 chan 关键字创建的,它只能传输相同类型的数据,不允许从同一通道传输不同类型的数据。

句法:

var Channel_name chan Type

您还可以使用 make()函数使用速记声明创建通道。

句法:

channel_name:= make(chan Type)

例子:

// Go program to illustrate
// how to create a channel
package main
  
import "fmt"
  
func main() {
  
    // Creating a channel
    // Using var keyword
    var mychannel chan int
    fmt.Println("Value of the channel: ", mychannel)
    fmt.Printf("Type of the channel: %T ", mychannel)
  
    // Creating a channel using make() function
    mychannel1 := make(chan int)
    fmt.Println("\nValue of the channel1: ", mychannel1)
    fmt.Printf("Type of the channel1: %T ", mychannel1)
}

输出:

Value of the channel:  
Type of the channel: chan int 
Value of the channel1:  0x432080
Type of the channel1: chan int 

从通道发送和接收数据

在 Go 语言中,通道工作有两个主要操作,一个是发送,另一个是接收,这两个操作统称为通信。而<-运算符的方向表示数据是接收还是发送。在通道中,发送和接收操作会阻塞,直到另一端默认没有准备好。它允许 goroutine 在没有显式锁或条件变量的情况下相互同步。

  1. 发送操作:发送操作用于在通道的帮助下将数据从一个 goroutine 发送到另一个 goroutine。像 int、float64 和 bool 这样的值可以安全且容易地通过通道发送,因为它们是复制的,因此不存在意外并发访问相同值的风险。同样,字符串也可以安全传输,因为它们是不可变的。但是对于通过通道发送指针或引用(如切片、映射等)是不安全的,因为指针或引用的值可能会通过发送 goroutine 或同时接收 goroutine 而改变,并且结果是不可预测的。因此,当您在通道中使用指针或引用时,您必须确保它们一次只能由一个 goroutine 访问。
    Mychannel <- element

    上面的语句表明数据(元素)在<-运算符的帮助下发送到通道(Mychannel)。

  2. 接收操作:接收操作用于接收发送运算符发送的数据。
    element := <-Mychannel

    上面的语句表示元素从通道(Mychannel)接收数据。如果接收到的结果语句不打算使用也是一个有效的语句。您还可以将接收语句编写为:

    <-Mychannel

例子:

// Go program to illustrate send
// and receive operation
package main
  
import "fmt"
  
func myfunc(ch chan int) {
  
    fmt.Println(234 + <-ch)
}
func main() {
    fmt.Println("start Main method")
    // Creating a channel
    ch := make(chan int)
  
    go myfunc(ch)
    ch <- 23
    fmt.Println("End Main method")
}

输出:

start Main method
257
End Main method

关闭频道

您还可以在 close()函数的帮助下关闭通道。这是一个内置函数,并设置一个标志,指示不会再向此通道发送任何值。

句法:

close()

您还可以使用 for range 循环关闭通道。在这里,接收器 goroutine 可以在给定语法的帮助下检查通道是打开还是关闭:

ele, ok:= <- Mychannel

在这里,如果 ok 的值为真,则表示通道已打开,则可以执行读取操作。如果 的值为 false,这意味着通道已关闭,则不会执行读取操作。

例子:

// Go program to illustrate how
// to close a channel using for
// range loop and close function
package main
  
import "fmt"
  
// Function
func myfun(mychnl chan string) {
  
    for v := 0; v < 4; v++ {
        mychnl <- "GeeksforGeeks"
    }
    close(mychnl)
}
  
// Main function
func main() {
  
    // Creating a channel
    c := make(chan string)
  
    // calling Goroutine
    go myfun(c)
  
    // When the value of ok is
    // set to true means the
    // channel is open and it
    // can send or receive data
    // When the value of ok is set to
    // false means the channel is closed
    for {
        res, ok := <-c
        if ok == false {
            fmt.Println("Channel Close ", ok)
            break
        }
        fmt.Println("Channel Open ", res, ok)
    }
}

输出:

Channel Open  GeeksforGeeks true
Channel Open  GeeksforGeeks true
Channel Open  GeeksforGeeks true
Channel Open  GeeksforGeeks true
Channel Close  false

要点

  • 阻塞发送和接收:在通道中,当数据发送到通道时,控制在该发送语句中被阻塞,直到其他 goroutine 从该通道读取。类似地,当一个通道从 goroutine 接收数据时,read 语句会阻塞直到另一个 goroutine 语句。
  • 零值通道:通道的零值为零。
  • Channel 中的 for 循环 for 循环可以迭代通道上发送的顺序值,直到它关闭。

    句法:

    for item := range Chnl { 
         // statements..
    }
    

    例子:

    // Go program to illustrate how to
    // use for loop in the channel
      
    package main
      
    import "fmt"
      
    // Main function
    func main() {
      
        // Creating a channel
        // Using make() function
        mychnl := make(chan string)
      
        // Anonymous goroutine
        go func() {
            mychnl <- "GFG"
            mychnl <- "gfg"
            mychnl <- "Geeks"
            mychnl <- "GeeksforGeeks"
            close(mychnl)
        }()
      
        // Using for loop
        for res := range mychnl {
            fmt.Println(res)
        }
    }
    

    输出:

    GFG
    gfg
    Geeks
    GeeksforGeeks
    
    
  • 通道长度:在通道中,您可以使用len()函数找到通道的长度。这里,长度表示在通道缓冲区中排队的值的数量。

    例子:

    // Go program to illustrate how to
    // find the length of the channel
      
    package main
      
    import "fmt"
    a
    // Main function
    func main() {
      
        // Creating a channel
        // Using make() function
        mychnl := make(chan string, 4)
        mychnl <- "GFG"
        mychnl <- "gfg"
        mychnl <- "Geeks"
        mychnl <- "GeeksforGeeks"
      
        // Finding the length of the channel
        // Using len() function
        fmt.Println("Length of the channel is: ", len(mychnl))
    }
    

    输出:

    Length of the channel is:  4
  • 频道容量:在频道中,您可以使用 cap()函数找到频道的容量。这里,容量表示缓冲区的大小。

    例子:

    // Go program to illustrate how to
    // find the capacity of the channel
      
    package main
      
    import "fmt"
      
    // Main function
    func main() {
      
        // Creating a channel
        // Using make() function
        mychnl := make(chan string, 5)
        mychnl <- "GFG"
        mychnl <- "gfg"
        mychnl <- "Geeks"
        mychnl <- "GeeksforGeeks"
      
        // Finding the capacity of the channel
        // Using cap() function
        fmt.Println("Capacity of the channel is: ", cap(mychnl))
    }
    

    输出:

    Capacity of the channel is:  5
  • Channel中的select和case语句:在go语言中,select语句就像一个没有任何输入参数的switch语句。此 select 语句用于在通道中执行 case 块提供的多个操作中的单个操作。