go语言中的channel(译)

243 阅读5分钟

这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战

原文链接:www.geeksforgeeks.org/channel-in-…

在go语言中,channel是一个协程跟另一个协程通信的媒介,并且这个通信是无锁的。换而言之,一个channel是允许一个协程发送数据给另一个协程的一项技术。默认情况下,channel是双向的,意味着协程可以通过同一个channel来发送或者接收数据,就像下面的图片展示的: image.png

Creating a Channel

在go语言中,一个channel可以使用chan关键字来创建,并且它只可以传输同一种类型的数据,不同类型的数据不允许在同一个channel中传输。

语法:

var Channel_name chan Type

也可以通过使用make()方法来创建一个channel,使用一种简单声明。

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 

从一个Channel发送和接收数据

在go语言中,channel跟两个主要原则的一起工作,一个是发送,另一个是接收,这两种操作共同称为通信。<-操作符说明数据是接收或者发送。在channel中,发送和接收操作默认会阻塞住,除非另一侧没有准备好。这允许协程互相同步而不需要明确的锁或者条件变量。

  1. 发送操作:发送操作是在channel的帮助下,用来从一个协程发送数据给另一个协程。像int,float64,bool类型的值等,可以安全和简单地来通过channel发送,因为它们是被复制的,所以同一个值,没有并发意外的风险。相似的,字符串也可以安全地被传输,因为它们是不可变的。但是像发送指针或者引用,如slice,map等,通过channel是不安全的,因为指针或引用的值可能会被发送或者被接收的协程同时改变,并且结果是不可预知的。因此,当你在channel中使用指针或者引用时,你必须确保同一时刻它们只会被一个协程访问。
Mychannel <- element

上面的语句表明,数据(element)发送到channel(Mychannel),通过<-的帮助。

  1. 接收操作:接收操作用来接收由发送操作符发送的数据。
element := <-Mychannel

上面的语句表名,element从channel(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

关闭channel

你也可以在close()的帮助下来关闭一个channel。这是一个内建方法,设置一个标识表明没有更多的值会发送给这个channel。

语法

close()

你也可以使用range循环来关闭channel。这里,接收者协程可以检查channel是开启或者关闭,通过这种语法:

ele, ok:= <- Mychannel

这里,如果ok的值是true,那么channel是开启的,读操作可以被执行。如果值是false,那就意味着channel是关闭的,读操作就无法执行。

例子

// 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

重要知识点

  • 阻塞发送和接收:在channel中,当数据发送给一个channel时,这个控制就被阻塞在那个发送操作,直到另一个协程读取了那个channel。同样的,当一个channel从这个协程接收数据时,读语句也会阻塞住,直到另一个协程语句。
  • 零值的channel:channel中的零值是nil
  • channel中的for循环:一个for循环可以按顺序迭代发送到channel的值,直到它被关闭了。 语法
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
  • channel的长度:在channel中,您可以使用len()函数来发现channel的长度。这里,length表明channel的缓冲区中的队列的值。 例子
// 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
  • channel的容量:在channel中,你可以使用cap()函数来获取channel的容量。这里,容量表明缓冲区的长度。 例子
// 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语句用在channel来执行由case阻塞提供的多操作中的一个单操作。