前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >管道(Channel)的读取与写入「让我们一起Golang」

管道(Channel)的读取与写入「让我们一起Golang」

作者头像
Regan Yue
发布2021-09-16 10:53:35
发布2021-09-16 10:53:35
71000
代码可运行
举报
文章被收录于专栏:ReganYue's BlogReganYue's Blog
运行总次数:0
代码可运行

管道(Channel)的读取与写入「让我们一起Golang」

我们都知道,协程是通过管道来进行通信、调度的。所以接下来我们引入管道的概念,通过管道可以来传递数据,协程与协程之间也可以通过管道来进行调度。

先来看一看第一段代码:

代码语言:javascript
代码运行次数:0
运行
复制
func main() {
	//创建一个管道
	ch := make(chan int)
	//子协程读数据
	go func() {
		x := <-ch
		fmt.Println("从管道内读数据:",x)
	}()
	//因为ch是int整形管道,往管道ch内只可写入整形数据,例如123。
	//主协程写数据
	ch <- 123
	time.Sleep(time.Second)
	fmt.Println("GAMEOVER")
}

这里是创建一个管道,然后用主协程往管道内写数据,然后从子协程往管道内读数据。

创建管道是用make,第一部分是chan,第二个部分是管道内数据的数据类型。因为没有给管道制定长度,所以默认为0。所以不能用于缓存。

该段程序是主协程往管道内写入123,然后子协程从管道内读出123.

运行结果是:

代码语言:javascript
代码运行次数:0
运行
复制
从管道内读数据: 123
GAMEOVER

那么思考一下,如果此段代码中将time.Sleep(time.Second)注释掉,也就是不使用这句代码不让主协程睡一秒,会出现什么情况?

是的,如果主协程不睡一秒的话,子协程可能还没读到数据,主协程就结束了,注意,主协程不是被杀死了,是正常结束了。

如果不睡一秒,而是使用runtime.Goexit()杀掉主协程,那么子协程就会失去约束,仍然会输出管道的数据。

如果主协程不写的话,我们从管道中读不到数据这样可以理解,但是你可能想不到的是,如果子协程不读的话,主协程也不能将数据成功写入管道中。

代码语言:javascript
代码运行次数:0
运行
复制
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()

它会报一个致命错误,它预判会产生死锁。

这说明管道不能存东西,它是没有缓存能力的,只能用于传输数据。

下面来讲一讲管道关闭之后的读写。

代码语言:javascript
代码运行次数:0
运行
复制
	//定义管道
	var ch chan int
	//make才能初始化
	ch  := make(chan int)
	//关闭管道
	close(ch)

如果只定义管道,那么管道ch为nil,此时不能关闭管道,此时关闭管道会报错。

只有将管道ch初始化之后,才能正常关闭管道。

不能重复关闭管道。

关闭会报错:

代码语言:javascript
代码运行次数:0
运行
复制
panic: close of closed channel

我们再来看一看关闭管道后再来读会怎么样咯~

代码语言:javascript
代码运行次数:0
运行
复制
func main()  {
	//定义管道
	var ch chan int
	//初始化管道,缓存能力为3
	ch  = make(chan int , 3)
	ch <- 123
	//关闭管道
	close(ch)
	go func() {
		x := <-ch
		fmt.Println("读到",x)

		//x,ok := <-ch
		//fmt.Println("读到",x,ok)
	}()

	time.Sleep(time.Second)
	fmt.Println("GAME OVER")
}

此段代码主协程中先关闭管道,然后再开辟子协程来读取管道中的数据。能不能读到呢?

先看一看结果:

代码语言:javascript
代码运行次数:0
运行
复制
读到 123
GAME OVER

读到了!

因为我们给管道的第二个参数设置为3,这就让管道有了缓存能力。而关闭管道之前已经将数据123存入了管道,之后再读取管道内数据是能够读取到的。

可以如果我们读取之后,再读一遍呢?会怎么样呢?

我们激活下面这段代码

代码语言:javascript
代码运行次数:0
运行
复制
x,ok := <-ch
fmt.Println("读到",x,ok)

得到的运行结果是:

代码语言:javascript
代码运行次数:0
运行
复制
读到 123读到 0 falseGAME OVER

则说明读取管道内的数据之后继续再读一遍是读到的默认数据0,无论你再读多少遍,读到都还是0。

如果管道关闭,我们还能像里面写数据吗?

是的,不能再往管道写数据了。

如果我们往管道里面写n个,那么关闭管道之后,再去读管道,就能再读n个,超过n个就读到的是默认值0。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 管道(Channel)的读取与写入「让我们一起Golang」
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档