勾哥:最近跟面试的兄弟们聊,仅仅懂 Java 确实不够。我上周在脉脉上看到有人吐槽:移动端开发开始内卷,面试难度非常高。其实 Java 不也如此么,大数据、人工智能,都要开始了解了。今年冬天真是不好过。。。

不过我还是想先看看 Go,Java 上有些东西用 Go 来实现,效果也不错。看现在趋势,没准儿以后 Go 会代替 Java 也说不定。哎,先学着吧。


协程

Go 语言中没有线程的概念,只有协程(goroutine)。相比线程来说,协程更加轻量,一个程序可以随意启动成千上万个 goroutine。

goroutine 被 Go runtime 所调度,这一点和线程不一样。也就是说,Go 语言的并发是由 Go 自己所调度的,自己决定同时执行多少个 goroutine,什么时候执行哪几个。这些对于我们开发者来说完全透明,只需要在编码的时候告诉 Go 语言要启动几个 goroutine,至于如何调度执行,我们不用关心。

要启动一个 goroutine 非常简单,Go 语言为我们提供了 go 关键字,相比其他编程语言简化了很多,如下面的代码所示:

func main() {   go fmt.Println("飞雪无情")   fmt.Println("我是 main goroutine")   time.Sleep(time.Second)}

这样就启动了一个 goroutine,用来调用 fmt.Println 函数,打印“飞雪无情”。所以这段代码里有两个 goroutine,一个是 main 函数启动的 main goroutine,一个是我自己通过 go 关键字启动的 goroutine。

从示例中可以总结出 go 关键字的语法,如下所示:

go function()

go 关键字后跟一个方法或者函数的调用,就可以启动一个 goroutine,让方法在这个新启动的 goroutine 中运行。

运行以上示例,可以看到如下输出:

我是 main goroutine飞雪无情

从输出结果也可以看出,程序是并发的,go 关键字启动的 goroutine 并不阻塞 main goroutine 的执行,所以我们才会看到如上打印结果。

小提示:示例中的 time.Sleep(time.Second) 表示等待一秒,这里是让 main goroutine 等一秒,不然 main goroutine 执行完毕程序就退出了,也就看不到启动的新 goroutine 中“飞雪无情”的打印结果了。

Channel

那么如果启动了多个 goroutine,它们之间该如何通信呢?这就是 Go 语言提供的 **channel(通道)**要解决的问题。

1. 声明一个 channel

在 Go 语言中,声明一个 channel 非常简单,使用内置的 make 函数即可,如下所示:

ch:=make(chan string)

其中 chan 是一个关键字,表示是 channel 类型。后面的 string 表示 channel 里的数据是 string 类型。通过 channel 的声明也可以看到,chan 是一个集合类型。

定义好 chan 后就可以使用了,一个 chan 的操作只有两种:发送和接收。

  • 接收:获取 chan 中的值,操作符为 

  • 发送:向 chan 发送值,把值放在 chan 中,操作符为 chan 。

小技巧:这里注意发送和接收的操作符,都是  ,只不过位置不同。接收的  操作符在 chan 的左侧,发送的  操作符在 chan 的右侧。

现在我把上个示例改造下,使用 chan 来代替 time.Sleep 函数的等待工作,如下面的代码所示:

func main() {   ch:=make(chan string)   go func() {      fmt.Println("飞雪无情")      ch "goroutine 完成"   }()   fmt.Println("我是 main goroutine")   v:=   fmt.Println("接收到的chan中的值为:",v)}

运行这个示例,可以发现程序并没有退出,可以看到 飞雪无情 的输出结果,达到了 time.Sleep 函数的效果,如下所示:

我是 main goroutine飞雪无情接收到的chan中的值为:goroutine 完成

可以这样理解:

  • 在上面的示例中,我们在新启动的 goroutine 中向 chan 类型的变量 ch 发送值;
  • 在 main goroutine 中,从变量 ch 接收值,如果 ch 中没有值,则阻塞等待到 ch 中有值可以接收为止。

为什么程序不会在新的 goroutine 完成之前退出?

因为通过 make 创建的 chan 中没有值,而 main goroutine 又想从 chan 中获取值,获取不到就一直等待,等到另一个 goroutine 向 chan 发送值为止。

channel 有点像在两个 goroutine 之间架设的管道,一个 goroutine 可以往这个管道里发送数据,另外一个可以从这个管道里取数据,有点类似于我们说的队列。

2. 无缓冲 channel

上面的示例中,使用 make 创建的 chan 就是一个无缓冲 channel,它的容量是 0,不能存储任何数据。所以无缓冲 channel 只起到传输数据的作用,数据并不会在 channel 中做任何停留。这也意味着,无缓冲 channel 的发送和接收操作是同时进行的,它也可以称为同步 channel。

3. 有缓冲 channel

有缓冲 channel 类似一个可阻塞的队列,内部的元素先进先出。通过 make 函数的第二个参数可以指定 channel 容量的大小,进而创建一个有缓冲 channel,如下面的代码所示:

cacheCh:=make(chan int,5)

我创建了一个容量为 5 的 channel,内部的元素类型是 int,也就是说这个 channel 内部最多可以存放 5 个类型为 int 的元素,如下图所示:

有缓冲 channel

一个有缓冲 channel 具备以下特点:

  • 有缓冲 channel 的内部有一个缓冲队列;

  • 发送操作是向队列的尾部插入元素,如果队列已满,则阻塞等待,直到另一个 goroutine 执行,接收操作释放队列的空间;

  • 接收操作是从队列的头部获取元素并把它从队列中删除,如果队列为空,则阻塞等待,直到另一个 goroutine 执行,发送操作插入新的元素。

因为有缓冲 channel 类似一个队列,可以获取它的容量和里面元素的个数。如下面的代码所示:

cacheCh:=make(chan int,5)cacheCh cacheCh fmt.Println("cacheCh容量为:",cap(cacheCh),",元素个数为:",len(cacheCh))

其中,通过内置函数 cap 可以获取 channel 的容量,也就是最大能存放多少个元素,通过内置函数 len 可以获取 channel 中元素的个数。

小提示:无缓冲 channel 其实就是一个容量大小为 0 的 channel。比如 make(chan int,0)

4. 关闭 channel

channel 还可以使用内置函数 close 关闭,如下面的代码所示:

close(cacheCh)

如果一个 channel 被关闭了,就不能向里面发送数据了,如果发送的话,会引起 painc 异常。但是还可以接收 channel 里的数据,如果 channel 里没有数据的话,接收的数据是元素类型的零值。

5. 单向 channel

有时候,我们有一些特殊的业务需求,比如限制一个 channel 只可以接收但是不能发送,或者限制一个 channel 只能发送但不能接收,这种 channel 称为单向 channel

单向 channel 的声明也很简单,只需要在声明的时候带上  操作符即可,如下面的代码所示:

onlySend := make(chanonlyReceive:=make(

注意,声明单向 channel  操作符的位置和上面讲到的发送和接收操作是一样的。

在函数或者方法的参数中,使用单向 channel 的较多,这样可以防止一些操作影响了 channel。

下面示例中的 counter 函数,它的参数 out 是一个只能发送的 channel,所以在 counter 函数体内使用参数 out 时,只能对其进行发送操作,如果执行接收操作,则程序不能编译通过。

unter(out chan  //函数内容使用变量out,只能进行发送操作}

6. select+channel 示例

假设要从网上下载一个文件,我启动了 3 个 goroutine 进行下载,并把结果发送到 3 个 channel 中。其中,哪个先下载好,就会使用哪个 channel 的结果。

在这种情况下,如果我们尝试获取第一个 channel 的结果,程序就会被阻塞,无法获取剩下两个 channel 的结果,也无法判断哪个先下载好。这个时候就需要用到多路复用操作了,在 Go 语言中,通过 select 语句可以实现多路复用,其语句格式如下:

select {case i1 =      //todocase c2  //tododefault: // default todo}

整体结构和 switch 非常像,都有 case 和 default,只不过 select 的 case 是一个个可以操作的 channel。

小提示:多路复用可以简单地理解为,N 个 channel 中,任意一个 channel 有数据产生,select 都可以监听到,然后执行相应的分支,接收数据并处理。

有了 select 语句,就可以实现下载的例子了。如下面的代码所示:

func main() {   //声明三个存放结果的channel   firstCh := make(chan string)   secondCh := make(chan string)   threeCh := make(chan string)   //同时开启3个goroutine下载   go func() {      firstCh "firstCh")   }()   go func() {      secondCh "secondCh")   }()   go func() {      threeCh "threeCh")   }()   //开始select多路复用,哪个channel能获取到值,   //就说明哪个最先下载好,就用哪个。   select {   case filePath :=       fmt.Println(filePath)   case filePath :=       fmt.Println(filePath)   case filePath :=       fmt.Println(filePath)   }}func downloadFile(chanName string) string {   //模拟下载文件,可以自己随机time.Sleep点时间试试   time.Sleep(time.Second)   return chanName+":filePath"}

如果这些 case 中有一个可以执行,select 语句会选择该 case 执行,如果同时有多个 case 可以被执行,则随机选择一个,这样每个 case 都有平等的被执行的机会。如果一个 select 没有任何 case,那么它会一直等待下去。

总结

在 Go 语言中,提倡通过通信来共享内存,而不是通过共享内存来通信,其实就是提倡通过 channel 发送接收消息的方式进行数据传递,而不是通过修改同一个变量。所以在数据流动、传递的场景中要优先使用 channel,它是并发安全的,性能也不错。


推荐阅读:下一代微服务架构凭什么要看Service Mesh

搞定Java并发:为什么说只有1种实现线程的方法?

搞定Java并发:为什么说只有1种实现线程的方法?(下)


go channel 缓冲区最大限制_Java内卷系列之你不得不知的Go并发基础相关推荐

  1. 程序员的996与内卷

    最近看到这个问题被谈得很多.铺天盖地的35岁.内卷化.996.这里也想谈谈自己的想法. 1)内卷化的形成 内卷为什么会形成呢?从公司内部的角度来说,同事之间做的事情也缺少独特性.那么既然每个人都差不多 ...

  2. 在“内卷”、“红海”的2020 年,开垦计算机视觉领域的知识荒原:BatchNorm

    作者丨纵横@知乎 来源丨https://zhuanlan.zhihu.com/p/166101119 2020 年已经要迎来了第二学期,内卷.红海.赛马似乎仍是 CV 领域的主旋律.不考虑 ddl 的 ...

  3. 内卷了!DAS、NAS、SAN区别和FC SAN存储

    内卷了!DAS.NAS.SAN区别和FC SAN存储 https://www.toutiao.com/i6943518812004172299/?tt_from=weixin&utm_camp ...

  4. 本周AI热点回顾:「时空版」Transformer训练速度远超3D CNN;拒绝内卷的AI狼火了!不想抓羊只想躺!...

    ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍点击左上方蓝字关注我们 01 「时空版」Transformer训练速度远超3D CNN,提速3倍! Facebook AI推出了全新的视频理解架构TimeSform ...

  5. 超越卷积、自注意力机制:强大的神经网络新算子involution(连算子都内卷= =)

    这篇工作主要是由我和SENet的作者胡杰一起完成的,也非常感谢HKUST的两位导师 陈启峰和张潼老师的讨论和建议. 本文是对我们CVPR 2021被接收的文章 Involution: Invertin ...

  6. involution内卷

    首先,本文章是基于 机器之心:超越卷积.自注意力:强大的神经网络新算子「内卷」解读,的一些个人总结这里放上B站链接 普通卷积 这是普通卷积,图上为取一个卷积核的其中一个通道的对目标的一个通道的卷积内容 ...

  7. 计算机视觉热点探讨:MLP,RepMLP,全连接与“内卷”

    点击上方"迈微AI研习社",选择"星标★"公众号 重磅干货,第一时间送达 本文转自丁霄汉@知乎,https://zhuanlan.zhihu.com/p/375 ...

  8. 清华提出RepMLP:FC“内卷”,卷出性能!

    点击下方卡片,关注"CVer"公众号 AI/CV重磅干货,第一时间送达 作者:丁霄汉  |  已授权转载(源:知乎) https://zhuanlan.zhihu.com/p/37 ...

  9. 热点讨论:MLP、RepMLP、全连接与“内卷”

    ‍ ‍点击上方"机器学习与生成对抗网络",关注星标 获取有趣.好玩的前沿干货! 作者:丁霄汉 本文地址:https://zhuanlan.zhihu.com/p/375422742 ...

最新文章

  1. 云原生架构演进与企业上云
  2. C++ Primer 5th笔记(chap 19 特殊工具与技术)定位 new 表达式
  3. JZOJ 5376. 【NOIP2017提高A组模拟9.19】Candy
  4. 基于FPGA的直流电机PWM控制+毕业论文
  5. python连接sqlserver、怎么跨表查询_python 连接sqlserver,mysql
  6. .NET控件Designer架构设“.NET研究”计
  7. Spring-mvc设置@RequestMapping标签更改返回头及@RequestMapping简述
  8. Session基础知识
  9. SQL Server 批量插入数据的两种方法 - 转
  10. 你必须知道的.NET
  11. latex做ppt_用Markdown可以做什么
  12. EPS格式转黑白照片(高清晰版本)
  13. dsolve函数的功能_MATLAB求解常微分方程:ode45函数与dsolve函数
  14. 程序员自我营销,如何打造个人品牌
  15. linux vi dd命令详解,Linux dd命令详解:数据备份,并在备份过程中进行格式转换...
  16. 大佬H5网页手机端怎么应用微信快捷登陆?
  17. matplotlib的imshow在Python shell IDLE环境无法显示图像问题
  18. 英文网站推广常用方法有哪些
  19. Lesson 16 Mary had a little lamb 内容鉴赏
  20. 虚拟pdn服务器,【转载】EPS中的PDN连接

热门文章

  1. 运维提升首选技能KubernetesPrometheus,你了解多少?(文末福利)
  2. HBase 在京东人资数据预处理平台中的实践!
  3. 基于 Kubernetes 的微服务项目设计与实现
  4. 有大佬通过研发这款Chrome插件的使用教程,赚了上百万! 网友:互联网的钱太好赚了~...
  5. 内存分页不就够了?为什么还要分段?
  6. Java中的5大队列,你知道几个?
  7. 图解|通用搜索引擎背后的技术点
  8. 强烈推荐8个良心好用的国产软件应用,让你爱不释手
  9. Redis的数据模型
  10. 质数判断及质因数分解 质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。 0和1不是质数 除了0,1,质数以外其他的数叫合数