前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang channel 快速入门

Golang channel 快速入门

作者头像
恋喵大鲤鱼
发布2020-07-27 16:30:55
5460
发布2020-07-27 16:30:55
举报
文章被收录于专栏:C/C++基础C/C++基础
在这里插入图片描述
在这里插入图片描述

简介

Go 中多个 Goroutine 间的通信和同步一般使用 channel 来完成。channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。

代码语言:javascript
复制
ch <- v    // 将 v 送入 channel ch
v := <-ch  // 从 ch 接收,并且赋值给 v

操作符 <- 形似箭头,其指向就是数据流的方向。

和 map 与 slice 一样,channel 使用前必须创建:

代码语言:javascript
复制
ch := make(chan int)

默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。

缓冲 channel

channel 有两种形式的,一种是无缓冲的,一个 Go 程向这个 channel 发送了消息后,会阻塞当前Go 程,直到其他 Go 程接收了这个 channel 中的消息。

channel 可以是带缓冲的,创建 channel 时可以指定缓冲的消息数量,当消息数量小于指定值时,不会出现阻塞,超过之后才会阻塞。为 make 提供第二个参数作为缓冲大小来初始化一个缓冲 channel:

代码语言:javascript
复制
ch := make(chan int, 100)

向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区为空的时候接收会阻塞。

代码语言:javascript
复制
package main

import "fmt"

func main() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

运行输出:

代码语言:javascript
复制
1
2

range 和 close 操作

发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过

代码语言:javascript
复制
v, ok := <-ch

之后 ok 会被设置为 false。

循环 for i := range c 会不断从 channel 接收值,直到它被关闭。

注意: 关闭 channel 应该由发送者而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。

还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 range。

select 操作

select 语句使得一个 goroutine 在多个通讯操作上等待。

select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。

代码语言:javascript
复制
package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

运行输出:

代码语言:javascript
复制
0
1
1
2
3
5
8
13
21
34
quit

为了非阻塞的发送或者接收,可使用 default 分支。当 select 中的其他条件分支都没有准备好的时候,default 分支会被执行。

代码语言:javascript
复制
package main

import (
	"fmt"
	"time"
)

func main() {
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(200 * time.Millisecond)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		default:
			fmt.Println("default")
			time.Sleep(50 * time.Millisecond)
		}
	}
}

运行输出:

代码语言:javascript
复制
default
default
tick.
default
default
tick.
BOOM!

注意要点

  • 读取被关闭的 channel,不会发生阻塞,剩余的数据仍可以取出;若为空,取出的值是对应类型的零值;
  • 向关闭的 channel 中写数据,无论是否带缓冲,都会引发 panic;
  • 不要从接收端关闭 channel,也不要在多个并发发送端中关闭 channel,而是在在唯一或最后一个发送端中关闭 channel,因为这样可以避免发生向一个已经关闭的 channel 发送值而引发 panic;
  • 关闭已经关闭的 channel 会导致 panic,避免重复关闭;

参考文献

[1] A Tour of Go

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-07-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 缓冲 channel
  • range 和 close 操作
  • select 操作
  • 注意要点
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档