您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

Go 语言 channel 的阻塞问题

时间:2022-01-04 15:55:24  来源:  作者:metashops

Hello,大家好,又见面了!上一遍我们将 channel 相关基础以及使用场景。这一篇,还需要再次进阶理解channel 阻塞问题。以下创建一个chan类型为int,cap 为3。

ch := make(chan string,1)

channel 内部其实是一个环形buf数据结构,是一种滑动窗口机制,当make完后,就分配在 Heap 上。

Channel 是如何发送数据和接收数据,会有什么问题出现,面试也是常见。

设 G1 为发送者:

ch <- "hello"

上面,向 chan 发送一条“hello”数据:

  • 第一步: acquire lock
  • 第二步:enqueue(把“hello”拷贝buf数组里)
  • 第三步:release lock

如果 G1 发送数据超过指定cap时,会出现什么情况?

看下面实例:

ch := make(chan int,1)
ch <- 100
ch <- 200

以上会出现什么,chan 缓冲区允许大小为1,如果再往chan仍数据,满了就会被阻塞,那么是如何实现阻塞的呢?当 chan 满时,会进入 gopark,此时 G1 进入一个 waiting 状态,然后会创建一个 sudog 对象,其实就sendq队列,把 200放进去。等 buf 不满的时候,再唤醒放入buf里面。

通过如下源码,你会更加清晰:

type hchan struct {
  //省略部分代码。。。
  recvq    waitq  // list of recv waiters
  sendq    waitq  // list of send waiters
  //省略部分代码。。。
}
type waitq struct {
  first *sudog
  last  *sudog
}
type sudog struct {
  g *g
  next *sudog
  prev *sudog
  elem unsafe.Pointer // data element (may point to stack)
  acquiretime int64
  releasetime int64
  ticket      uint32
  isSelect bool
  success bool
  parent   *sudog // semaRoot binary tree
  waitlink *sudog // g.waiting list or semaRoot
  waittail *sudog // semaRoot
  c        *hchan // channel
}

设 G2 为接收者:

d := <- ch

上面,从 chan 获取数据:

  • 第一步:也是先获取锁
  • 第二步:从 buf 数据里面拷贝,赋给 d 变量
  • 第三步:release lock

Go 语言核心思想:“Do not communicate by sharing memory; instead, share memory by communicating.” 你可以看看这本书名叫:Effective Go

如果接收者,接收一个空对象,也会发生什么情况?

代码示例

ch := make(chan int,1)
t := <- ch

也会报错如下:

fatal error: all goroutines are asleep - deadlock!

上面,从 chan 取出数据,可是没有数据了。此时,它会把 接收者 G2 阻塞掉,也是和G1发送者一样,也会执行 gopark 将状态改为 waiting,不一样的点就是。

正常情况下,接收者G2作为取出数据是去 buf 读取数据的,但现在,buf 为空了,此时,接收者G2会将sudog导出来,因为现在G2已经被阻塞了嘛,会把G2给G,然后将t := <-ch中变量 t 是在栈上的地址,放进去elem,也就是说,只存它的地址指针在sudog里面。

最后,ch <- 200 当G1往 chan 添加200这个数据,正常情况是将数据添加到buf里面,然后唤醒 G2 是吧,而现在是将 G1 的添加200数据直接干到刚才G2阻塞的t这里变量里面。

你会认为,这样真的可以吗?想一想,G2 本来就是已经阻塞了,然后我们直接这么干肯定没有什么毛病,而且效率提高了,不需要再次放入buf再取出,这个过程也是需要时间。不然,不得往chan添加数据需要加锁、拷贝、解锁一序列操作,那肯定就慢了,我想Go语言是为了高效及内存使用率的考虑这样设计的。(注意,一般都是在runtime里面完成,不然会出现象安全问题。)

总结

chan 类型的特点:chan 如果为空,receiver 接收数据的时候就会阻塞等待,直到 chan 被关闭或者有新的数据到来。有这种个机制,就可以实现 wait/notify 的设计模式。

使用 channel 要思考的问题?

  • 如果N个发送者发送到chan及N个接收者,这样会频繁导致锁频繁争用;
  • 如果N个往 chan 发送,而 buf很小,会导致 Goroutine 不断被gopark,然后runtime开销就大了;

相关面试题:

  • channel 有缓冲区和无缓冲区别?
  • channel 线程安全吗?
  • 是否了解 channel 底层实现,比如channel 底层数据结构?环形buf


Tags:Go 语言   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Hello,大家好,又见面了!上一遍我们将 channel 相关基础以及使用场景。这一篇,还需要再次进阶理解channel 阻塞问题。以下创建一个chan类型为int,cap 为3。ch := make(chan string...【详细内容】
2022-01-04  Tags: Go 语言  点击:(0)  评论:(0)  加入收藏
作为开发人员,微信生态我们不能无视,微信提供的开放能力,我们应该有所了解。微信支付作为重要的一部分,平时工作中可能难免会遇到。Go 作为一门新语言,微信支付没有提供 Go 的 SD...【详细内容】
2020-09-04  Tags: Go 语言  点击:(53)  评论:(0)  加入收藏
Go语言开发团队花了很长时间来解决当今软件开发人员面对的问题。开发人员在为项目选择语言时,不得不在快速开发和性能之间做出选择。C和C++这类语言提供了很快的执行速度,而Ru...【详细内容】
2020-08-27  Tags: Go 语言  点击:(56)  评论:(0)  加入收藏
在 2 世纪, 发送机密消息的一个有效方法就是对每个字母进行位移, 使得 &#39;a&#39; 变为 &#39;d&#39; , &#39;b&#39; 变为 &#39;e&#39; , 依次类推。 这样处理产生的结果看上去...【详细内容】
2020-08-11  Tags: Go 语言  点击:(61)  评论:(0)  加入收藏
大家好,从今天起,我们一起来学习 Echo 框架。这几年,随着 Go 语言的发展,各种 Web 框架也出现了。常言道:没有选择是一种无奈,有选择是一种痛苦。所以,大家总是问:Web 框架大佬推荐...【详细内容】
2020-06-30  Tags: Go 语言  点击:(82)  评论:(0)  加入收藏
背景介绍直播系统主要是以内容为主,好的内容可以吸引用户来欣赏,也能为公司带来可观的收益,既然有传播的入口,那么必然有负面内容的出现,随着平台用户量不断扩大,内容的监管也是必...【详细内容】
2020-06-02  Tags: Go 语言  点击:(60)  评论:(0)  加入收藏
每到节假日和过年,需要外出通行的人几乎都会遇到一个问题:抢火车票!当全国上亿人都在固定的时间段抢票,服务器动辄就要承受上百万级并发的情况时,你就会明白,一个支持高并发的服务...【详细内容】
2020-05-10  Tags: Go 语言  点击:(63)  评论:(0)  加入收藏
编写健壮且高性能的网络服务需要付出大量的努力。提高服务性能的方式有很多种,比如优化应用层的代码,更进一步,还可以看看垃圾回收器,操作系统,网络传输,以及部署我们服务的硬件是否有优化空间。...【详细内容】
2019-12-30  Tags: Go 语言  点击:(77)  评论:(0)  加入收藏
反射是 Go 语言比较重要的一个特性之一,虽然在大多数的应用和服务中并不常见,但是很多框架都依赖 Go 语言的反射机制实现一些动态的功能。作为一门静态语言,Golang 在设计上都...【详细内容】
2019-11-20  Tags: Go 语言  点击:(195)  评论:(0)  加入收藏
我终于又开始使用 Go 语言编程了。虽然我在前两年多的时间里积极参与这个项目,但从 2012 年起,我就基本没有参加过这个项目。最初,我之所以做出贡献,是因为我是贝尔实验室 Plan...【详细内容】
2019-09-23  Tags: Go 语言  点击:(113)  评论:(0)  加入收藏
▌简易百科推荐
Hello,大家好,又见面了!上一遍我们将 channel 相关基础以及使用场景。这一篇,还需要再次进阶理解channel 阻塞问题。以下创建一个chan类型为int,cap 为3。ch := make(chan string...【详细内容】
2022-01-04  metashops    Tags:Go 语言   点击:(0)  评论:(0)  加入收藏
Go 泛型定了,有哪些好的使用场景,哪些不好的应用场景,亦或哪些使用看起来丑?本文聊聊这个问题。01 简介泛型很棒,而且 Go 变得比以前更方便了。但是与可能非常有用的 channel 类...【详细内容】
2021-12-29    Go语言中文网  Tags:泛型   点击:(10)  评论:(0)  加入收藏
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(15)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(29)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(32)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(203)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(186)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(128)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(235)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(227)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条