Go语言的通道(1)-无缓冲通道
前言:
上文中我们采用了【原子函数】已经【共享锁】两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel)。
一、通道是什么?
其实无论是原子函数还是共享锁都是通过共享内存的方式进行的同步、效率一般不高,而Go语言中则使用了通道,它是一种通过传递信息的方式进行数据同步,通过发送和接收需要共享的资源,在goroutine 之间做同步。可以把通道看作是Goroutine之间的桥梁。
例1:创建一个通道
// 无缓冲的整型通道
unbuffered := make(chan int)
// 有缓冲的字符串通道
buffered := make(chan string, )
通道分为有缓冲和无缓冲的通道。
创建一个Channel的关键点:1.使用make创建 2.使用chan来告诉make我要创建的是通道 3.要告诉通道我要建立什么类型的通道。
例2:向通道发送值和接受值
// 有缓冲的字符串通道
buffered := make(chan string, )
// 通过通道发送一个字符串
buffered <- "Gopher"
// 从通道接收一个字符串
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进行数据交换
package ChannelDemo import (
"fmt"
"math/rand"
"sync"
"time"
) var wg sync.WaitGroup func init() {
rand.Seed(time.Now().UnixNano())
} func PlayTennis() {
court := make(chan int)
wg.Add()
//启动了两个协程,一个纳达尔一个德约科维奇
go player("纳达尔", court)
go player("德约科维奇", court) //将1放到通道中,模拟开球
court <-
wg.Wait()
} func player(name string, court chan int) {
defer wg.Done()
for {
// 将数据从通道中取出
ball, ok := <-court
if !ok {
fmt.Printf("选手 %s 胜利\n", name)
return
} //获取一个随机值,如果可以整除13,就让一个人没有击中,进而关闭整个通道
n := rand.Intn()
if n% == {
fmt.Printf("选手 %s 没接到\n", name)
close(court)
return
}
//如果击中球,就将击球的数量+1,放回通道中
fmt.Printf("选手 %s 击中 %d\n", name, ball)
ball++
court <- ball
}
}
执行结果(每次会有变化):
选手 纳达尔 击中
选手 德约科维奇 击中
选手 纳达尔 击中
选手 德约科维奇 击中
选手 纳达尔 击中
选手 德约科维奇 击中
选手 纳达尔 击中
选手 德约科维奇 击中
选手 纳达尔 没接到
选手 德约科维奇 胜利
ok 标志是否为false。如果这个值是false,表示通道已经被关闭,游戏结束。
下面这个例子,模拟里一个接力赛,也就是协程之间的传递的另一种形式
package ChannelDemo import (
"fmt"
"sync"
"time"
) var runnerWg sync.WaitGroup func Running() {
//创建一个“接力棒”,也就是通道
baton := make(chan int)
runnerWg.Add()
//创建第一个跑步走
go Runner(baton)
//开始跑
baton <-
runnerWg.Wait()
} func Runner(baton chan int) {
var newRunner int //选手接过接力棒
runner := <-baton
fmt.Printf("第 %d 选手接棒 \n", runner) //如果不是第四名选手,那么说明比赛还在继续
if runner != {
//创建一名新选手
newRunner = runner +
fmt.Printf("第 %d 准备接棒 \n", newRunner)
go Runner(baton)
} //模拟跑步
time.Sleep( * time.Millisecond)
//如果第四名跑完了,就结束
if runner == {
fmt.Printf("第 %d 结束赛跑 \n", runner)
runnerWg.Done()
return
} fmt.Printf("第 %d 选手和第 %d 选手交换了接力棒 \n",
runner,
newRunner) //选手递出接力棒
baton <- newRunner
}
运行结果:
第 名选手接棒
第 名选手准备接棒
第 名选手将接力棒递给第 名选手
第 名选手接棒
第 名选手准备接棒
第 名选手将接力棒递给第 名选手
第 名选手接棒
第 名选手准备接棒
第 名选手将接力棒递给第 名选手
第 名选手接棒
第 名选手冲线,比赛结束
三、无缓冲通道小结
我在看例子的过程中,其实遇到的问题在于,我没有理解goroutine是怎么进行交换的,我以为是goroutine有一个集合一样的结构在通道外面等待取数据,这样就存在我刚拿完再那的情况。就像下面这个图显示一样

但是实际情况应该像下面

Go1写入通道锁住的Go1、Go2读出进入通道锁住Go2,只有Go1写完Go2取完才能释放,但是像上面第一个例子代码,读出之后马上就写入,所以对于这样的协程其实一直是锁住的状态。两个协程就通过这种方式进行数据的传递。
Go语言的通道(1)-无缓冲通道的更多相关文章
- Golang并发编程有缓冲通道和无缓冲通道(channel)
无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...
- golang channel无缓冲通道会发生阻塞的验证
公司搞了午间技术par,本周我讲的主题是关于无缓冲通道channel是否会发生阻塞,并进行了验证. go语言中channel分为无缓冲通道和有缓冲通道两种 channel提供了一种在goroutine ...
- [Go] golang无缓冲通道实现工作池控制并发
展示如何使用无缓冲的通道创建一个goroutine池,控制并发频率1.无缓冲通道保证了两个goroutine之间的数据交换2.当所有的goroutine都忙的时候,能够及时通过通道告知调用者3.无缓冲 ...
- [GO]无缓冲通道(unbuffered channel)
无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...
- golang中为何在同一个goroutine中使用无缓冲通道会导致死锁
package main import "fmt" func main() { /* 以下程序会导致死锁 c := make(chan int) c <- 10 n1 := ...
- go无缓冲通道
package main import ( "fmt" "math/rand" "sync" "time" ) //wg ...
- go语言之进阶篇无缓冲channel
1.无缓冲channel 示例: package main import ( "fmt" "time" ) func main() { //创建一个无缓存的ch ...
- Go语言有缓冲和无缓冲通道实现样例
感觉可以,但不好用. 应该有封装程序更高的包包吧. package main import ( "math/rand" "fmt" "time&quo ...
- go之无缓冲channel(通道)和有缓冲channel(通道)
channel我们先来看一下通道的解释:channel是Go语言中的一个核心类型,可以把它看成管道.并发核心单元通过它就可以发送或者接收数据进行通讯,这在一定程度上又进一步降低了编程的难度.chann ...
随机推荐
- 【代码笔记】Web-CSS-CSS id和Class选择器
一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- 升级本地部署的CRM到Dynamics 365及部分新特性介绍。
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复241或者20161226可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- 注意,更改团队所属业务部门用Update消息无效!
摘要: 本人微信公众号:微软动态CRM专家罗勇 ,回复297或者20190112可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me ...
- 用 Heapster 监控集群 - 每天5分钟玩转 Docker 容器技术(176)
Heapster 是 Kubernetes 原生的集群监控方案.Heapster 以 Pod 的形式运行,它会自动发现集群节点.从节点上的 Kubelet 获取监控数据.Kubelet 则是从节点上的 ...
- libstdc++适配Xcode10与iOS12
编译报错 当你开心得升级完新 macOS,以及新 XCode,准备体验了一把 Dark Mode 编程模式,开心的打开自己的老项目的时候,发现编译不通过了╮(╯_╰)╭ 如果你的工程中如果依赖 lib ...
- Linux学习历程——Centos 7 uptime 、free命令
一.命令介绍 uptime命令 uptime命令用于查看系统负载信息以及系统运行时间等. free命令 free命令用于查看当前系统中内存使用量信息. 二.实例 uptime命令实例 直接运行 upt ...
- 使用laravel-admin后台sdk报错Failed to load resource: net::ERR_CERT_AUTHORITY_INVALID、Provisional headers are shown
报错Failed to load resource: net::ERR_CERT_AUTHORITY_INVALID请先确定自己的资源url是否可以确实访问到(地址正确与否.访问权限是否开启等) 若n ...
- 重装助手教你如何禁用Windows 10快速启动
快速启动是首先在Windows 8中实现并延续到Windows 10的功能,可在启动PC时提供更快的启动时间.它是一个方便的功能,也是大多数人在不知情的情况下使用的功能,但还有一些功能会在他们掌握新P ...
- C++面向对象程序设计之类和对象的特性
类和对象的属性 注意:本文为书籍摘要版,适合有一定程序基础的人阅读. 2.1 面向对象程序设计方法概述 2.1.1 什么是面向对象的程序设计 1.对象 客观世界中的任何一个事物都可以看成一个对象. 如 ...
- 转 Angular2优质学习资源收集
文档博客书籍类 官方网站: https://angular.io 中文站点: https://angular.cn Victor的blog(Victor是Angular路由模块的作者): https: ...