如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制。一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。
10年的亚东网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。全网整合营销推广的优势是能够根据用户设备显示端的尺寸不同,自动调整亚东建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“亚东网站设计”,“亚东网站推广”以来,每个客户项目都认真落实执行。
Go语言提倡使用通信的方法代替共享内存,当一个资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。
这里通信的方法就是使用通道(channel),如下图所示。
图:goroutine 与 channel 的通信
在地铁站、食堂、洗手间等公共场所人很多的情况下,大家养成了排队的习惯,目的也是避免拥挤、插队导致的低效的资源使用和交换过程。代码与数据也是如此,多个 goroutine 为了争抢数据,势必造成执行的低效率,使用队列的方式是最高效的,channel 就是一种队列一样的结构。
Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。
通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。
通道本身需要一个类型进行修饰,就像切片类型需要标识元素类型。通道的元素类型就是在其内部传输的数据类型,声明如下:
var 通道变量 chan 通道类型
chan 类型的空值是 nil,声明后需要配合 make 后才能使用。
通道是引用类型,需要使用 make 进行创建,格式如下:
通道实例 := make(chan 数据类型)
请看下面的例子:
ch1 := make(chan int) // 创建一个整型类型的通道 ch2 := make(chan interface{}) // 创建一个空接口类型的通道, 可以存放任意格式 type Equip struct{ /* 一些字段 */ } ch2 := make(chan *Equip) // 创建Equip指针类型的通道, 可以存放*Equip
通道创建后,就可以使用通道进行发送和接收操作。
通道的发送使用特殊的操作符<-
,将数据通过通道发送的格式为:
通道变量 <- 值
使用 make 创建一个通道后,就可以使用<-
向通道发送数据,代码如下:
// 创建一个空接口通道 ch := make(chan interface{}) // 将0放入通道中 ch <- 0 // 将hello字符串放入通道中 ch <- "hello"
把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。Go 程序运行时能智能地发现一些永远无法发送成功的语句并做出提示,代码如下:
package main func main() { // 创建一个整型通道 ch := make(chan int) // 尝试将0通过通道发送 ch <- 0 }
运行代码,报错:
fatal error: all goroutines are asleep - deadlock!
报错的意思是:运行时发现所有的 goroutine(包括main)都处于等待 goroutine。也就是说所有 goroutine 中的 channel 并没有形成发送和接收对应的代码。
通道接收同样使用<-
操作符,通道接收有如下特性:
① 通道的收发操作在不同的两个 goroutine 间进行。
由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。
② 接收将持续阻塞直到发送方发送数据。
如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。
③ 每次接收一个元素。
通道一次只能接收一个数据元素。
通道的数据接收一共有以下 4 种写法。
阻塞模式接收数据时,将接收变量作为<-
操作符的左值,格式如下:
data := <-ch
执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
data, ok := <-ch
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行,可以参见后面的内容。
阻塞接收数据后,忽略从通道返回的数据,格式如下:
<-ch
执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。
使用通道做并发同步的写法,可以参考下面的例子:
package main import ( "fmt" ) func main() { // 构建一个通道 ch := make(chan int) // 开启一个并发匿名函数 go func() { fmt.Println("start goroutine") // 通过通道通知main的goroutine ch <- 0 fmt.Println("exit goroutine") }() fmt.Println("wait goroutine") // 等待匿名goroutine <-ch fmt.Println("all done") }
执行代码,输出如下:
wait goroutine
start goroutine
exit goroutine
all done
代码说明如下:
通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下:
for data := range ch { }
通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。
遍历通道数据的例子请参考下面的代码。
使用 for 从通道中接收数据:
package main import ( "fmt" "time" ) func main() { // 构建一个通道 ch := make(chan int) // 开启一个并发匿名函数 go func() { // 从3循环到0 for i := 3; i >= 0; i-- { // 发送3到0之间的数值 ch <- i // 每次发送完时等待 time.Sleep(time.Second) } }() // 遍历接收通道数据 for data := range ch { // 打印通道数据 fmt.Println(data) // 当遇到数据0时, 退出接收循环 if data == 0 { break } } }
执行代码,输出如下:
3
2
1
0
代码说明如下:
网页题目:创新互联GO教程:Go语言通道(chan)——goroutine之间通信的管道
转载来于:http://www.mswzjz.cn/qtweb/news48/261448.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能