前言:

上文中我们采用了【原子函数】已经【共享锁】两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel)。

一、通道是什么?

其实无论是原子函数还是共享锁都是通过共享内存的方式进行的同步、效率一般不高,而Go语言中则使用了通道,它是一种通过传递信息的方式进行数据同步,通过发送和接收需要共享的资源,在goroutine 之间做同步。可以把通道看作是Goroutine之间的桥梁。

例1:创建一个通道

  1. // 无缓冲的整型通道
  2. unbuffered := make(chan int)
  3. // 有缓冲的字符串通道
  4. buffered := make(chan string, )

通道分为有缓冲和无缓冲的通道。

创建一个Channel的关键点:1.使用make创建 2.使用chan来告诉make我要创建的是通道 3.要告诉通道我要建立什么类型的通道。

例2:向通道发送值和接受值

  1. // 有缓冲的字符串通道
  2. buffered := make(chan string, )
  3. // 通过通道发送一个字符串
  4. buffered <- "Gopher"
  5. // 从通道接收一个字符串
  6. value := <-buffered

这个例子中创建了一个string类型的Channel,并向通道内传递了一个“Gopher”字符串,这里是通过<-进行传入的,然后通过<-这个方式把值放到value当中。

这里我的理解 <-就好比是一个赋值符号,无论是把值传递到Channel中,还是把Channel中的值传出来,都是将右边的值给左边

二、通道的种类

由上面的例如1,可以看到Channel也是有多种的,分为无缓冲通道和有缓冲通道,下面就简单总结一下两种类型的通道。

1.无缓冲通道

无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送goroutine 和接收goroutine 同时准备好,才能完成发送和接收操作。

上面的图很好的解释了通道和Goroutine的关系

1.左右两个goroutine都没有将手放到通道中。

2.左边的Goroutine将手放到了通道中,模拟了将数据放入通道,此时goroutine会被锁住

3.右边的Goroutine也将手放到了通道中,模拟了从通道中取出数据,同样进入了通道也会被锁住

4.两者通过通道执行数据的交换

5.交换完成

6.两者将手从通道中拿出,模拟了被锁住的goroutine被释放

下面这个程序,模拟了两个人打网球,很好的模拟了两个协程间通过channel进行数据交换

  1. package ChannelDemo
  2.  
  3. import (
  4.   "fmt"
  5.   "math/rand"
  6.   "sync"
  7.   "time"
  8. )
  9.  
  10. var wg sync.WaitGroup
  11.  
  12. func init() {
  13. rand.Seed(time.Now().UnixNano())
  14. }
  15.  
  16. func PlayTennis() {
  17. court := make(chan int)
  18. wg.Add()
  19. //启动了两个协程,一个纳达尔一个德约科维奇
  20. go player("纳达尔", court)
  21. go player("德约科维奇", court)
  22.  
  23. //将1放到通道中,模拟开球
  24. court <-
  25. wg.Wait()
  26. }
  27.  
  28. func player(name string, court chan int) {
  29. defer wg.Done()
  30. for {
  31. // 将数据从通道中取出
  32. ball, ok := <-court
  33. if !ok {
  34. fmt.Printf("选手 %s 胜利\n", name)
  35. return
  36. }
  37.  
  38. //获取一个随机值,如果可以整除13,就让一个人没有击中,进而关闭整个通道
  39. n := rand.Intn()
  40. if n% == {
  41. fmt.Printf("选手 %s 没接到\n", name)
  42. close(court)
  43. return
  44. }
  45. //如果击中球,就将击球的数量+1,放回通道中
  46. fmt.Printf("选手 %s 击中 %d\n", name, ball)
  47. ball++
  48. court <- ball
  49. }
  50. }

执行结果(每次会有变化):

  1. 选手 纳达尔 击中
  2. 选手 德约科维奇 击中
  3. 选手 纳达尔 击中
  4. 选手 德约科维奇 击中
  5. 选手 纳达尔 击中
  6. 选手 德约科维奇 击中
  7. 选手 纳达尔 击中
  8. 选手 德约科维奇 击中
  9. 选手 纳达尔 没接到
  10. 选手 德约科维奇 胜利

ok 标志是否为false。如果这个值是false,表示通道已经被关闭,游戏结束。

下面这个例子,模拟里一个接力赛,也就是协程之间的传递的另一种形式

  1. package ChannelDemo
  2.  
  3. import (
  4. "fmt"
  5. "sync"
  6. "time"
  7. )
  8.  
  9. var runnerWg sync.WaitGroup
  10.  
  11. func Running() {
  12. //创建一个“接力棒”,也就是通道
  13. baton := make(chan int)
  14. runnerWg.Add()
  15. //创建第一个跑步走
  16. go Runner(baton)
  17. //开始跑
  18. baton <-
  19. runnerWg.Wait()
  20. }
  21.  
  22. func Runner(baton chan int) {
  23. var newRunner int
  24.  
  25. //选手接过接力棒
  26. runner := <-baton
  27. fmt.Printf("第 %d 选手接棒 \n", runner)
  28.  
  29. //如果不是第四名选手,那么说明比赛还在继续
  30. if runner != {
  31. //创建一名新选手
  32. newRunner = runner +
  33. fmt.Printf("第 %d 准备接棒 \n", newRunner)
  34. go Runner(baton)
  35. }
  36.  
  37. //模拟跑步
  38. time.Sleep( * time.Millisecond)
  39. //如果第四名跑完了,就结束
  40. if runner == {
  41. fmt.Printf("第 %d 结束赛跑 \n", runner)
  42. runnerWg.Done()
  43. return
  44. }
  45.  
  46. fmt.Printf("第 %d 选手和第 %d 选手交换了接力棒 \n",
  47. runner,
  48. newRunner)
  49.  
  50. //选手递出接力棒
  51. baton <- newRunner
  52. }

运行结果:

  1. 名选手接棒
  2. 名选手准备接棒
  3. 名选手将接力棒递给第 名选手
  4. 名选手接棒
  5. 名选手准备接棒
  6. 名选手将接力棒递给第 名选手
  7. 名选手接棒
  8. 名选手准备接棒
  9. 名选手将接力棒递给第 名选手
  10. 名选手接棒
  11. 名选手冲线,比赛结束

三、无缓冲通道小结

我在看例子的过程中,其实遇到的问题在于,我没有理解goroutine是怎么进行交换的,我以为是goroutine有一个集合一样的结构在通道外面等待取数据,这样就存在我刚拿完再那的情况。就像下面这个图显示一样

但是实际情况应该像下面

Go1写入通道锁住的Go1、Go2读出进入通道锁住Go2,只有Go1写完Go2取完才能释放,但是像上面第一个例子代码,读出之后马上就写入,所以对于这样的协程其实一直是锁住的状态。两个协程就通过这种方式进行数据的传递。

Go语言的通道(1)-无缓冲通道的更多相关文章

  1. Golang并发编程有缓冲通道和无缓冲通道(channel)

    无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...

  2. golang channel无缓冲通道会发生阻塞的验证

    公司搞了午间技术par,本周我讲的主题是关于无缓冲通道channel是否会发生阻塞,并进行了验证. go语言中channel分为无缓冲通道和有缓冲通道两种 channel提供了一种在goroutine ...

  3. [Go] golang无缓冲通道实现工作池控制并发

    展示如何使用无缓冲的通道创建一个goroutine池,控制并发频率1.无缓冲通道保证了两个goroutine之间的数据交换2.当所有的goroutine都忙的时候,能够及时通过通道告知调用者3.无缓冲 ...

  4. [GO]无缓冲通道(unbuffered channel)

    无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...

  5. golang中为何在同一个goroutine中使用无缓冲通道会导致死锁

    package main import "fmt" func main() { /* 以下程序会导致死锁 c := make(chan int) c <- 10 n1 := ...

  6. go无缓冲通道

    package main import ( "fmt" "math/rand" "sync" "time" ) //wg ...

  7. go语言之进阶篇无缓冲channel

    1.无缓冲channel 示例: package main import ( "fmt" "time" ) func main() { //创建一个无缓存的ch ...

  8. Go语言有缓冲和无缓冲通道实现样例

    感觉可以,但不好用. 应该有封装程序更高的包包吧. package main import ( "math/rand" "fmt" "time&quo ...

  9. go之无缓冲channel(通道)和有缓冲channel(通道)

    channel我们先来看一下通道的解释:channel是Go语言中的一个核心类型,可以把它看成管道.并发核心单元通过它就可以发送或者接收数据进行通讯,这在一定程度上又进一步降低了编程的难度.chann ...

随机推荐

  1. css direction 属性简介与实际应用。

    目前正在用vue构建组件库.写到弹框的时候没想到按钮的顺序问题,但是在应用中,确实会有选项按钮顺序不同的情况发生,但是又想共用一个组件.那么问题就出现了.后来看到了这篇文章,才茅塞顿开. direct ...

  2. 仿9GAG制作过程(三)

    有话要说: 这次准备讲述后台服务器的搭建以及前台访问到数据的过程. 成果: 准备: 安装了eclipse 安装了Tomcat7 安装了数据库管理工具:Navicat 搭建服务器: 用eclipse直接 ...

  3. IE8环境下的上传图片预览

    今天做一个需要在IE浏览器上使用的信息录入项目,遇到了图片上传预览的问题,找了一些资料,最后使用了IE自带的滤镜做到了 <!--HTML IE8不支持opacity,只能使用双层,一层背景半透明 ...

  4. ATL右键文件菜单

    自己写的小程序中用到的,网上资料相对还是毕竟全的,这里再整理下.毕竟我也不是很了解ATL,里面估计还是有不少问题的,就当作参考吧. 1.创建ATL工程,这个没什么好讲的. 我对COM组件没什么研究,这 ...

  5. centos7防火墙导致不能访问的

    CentOS 7.0默认使用的是firewall作为防火墙,使用iptables必须重新设置一下 1.直接关闭防火墙 systemctl stop firewalld.service #停止firew ...

  6. ansible学习基础知识和模块(一)

    基础知识补充: 常用自动化运维工具 Ansible:使用python来开发的,无需设置Agentless(代理),一般管理几百台.与ssh的方式也不一样,ssh是基于c/s模式(客户端+服务器)来使用 ...

  7. Saltstack_使用指南07_远程执行-执行模块

    1. 主机规划 远程执行教程文档 https://docs.saltstack.com/en/latest/topics/tutorials/modules.html 所有模块文档 https://d ...

  8. APACHE SPARK 2.0 API IMPROVEMENTS: RDD, DATAFRAME, DATASET AND SQL

    What’s New, What’s Changed and How to get Started. Are you ready for Apache Spark 2.0? If you are ju ...

  9. Java多线程——中断机制

    前言:在Java多线程中,中断一直围绕着我们,当我们阅读各种关于Java多线程的资料.书籍时,“中断”一词总是会出现,笔者对其的理解也是朦朦胧胧,因此非常有必要搞清楚Java多线程的中断机制. 1.J ...

  10. 正则表达式regex(golang版)

    代码: //File: main.go package main import ( "fmt" "regexp" ) func main() { r := re ...