Golang 通道

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

Golang中的通道

创建一个通道

在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这样的值可以安全而容易地通过通道发送,因为它们是复制的,所以没有意外地同时访问同一个值的风险。同样地,字符串也可以安全地传输,因为它们是不可变的。但是对于通过通道发送指针或引用,如slice、map等,是不安全的,因为指针或引用的值可能被发送的goroutine或接收的goroutine同时改变,其结果是无法预测的。因此,当你在通道中使用指针或引用时,你必须确保它们在同一时间只能被一个goroutine访问。
Mychannel <- element

上面的语句表明,在** <- **操作符的帮助下,数据(元素)被发送到通道(Mychannel)。

  1. 接收操作:接收操作是用来接收由发送操作者发送的数据。
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的值为真,意味着通道是开放的,所以可以进行读操作。而如果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接收数据时,读语句被阻塞,直到另一个goroutine语句。
  • 零值通道:该通道的零值为零。
  • 通道中的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
  • 通道中的选择和case语句:在go语言中,选择语句就像switch语句,没有任何输入参数。这个选择语句在通道中被用来执行case块提供的多个操作中的一个操作。