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 ...
随机推荐
- vue 获取页面详情后 切换页面时 如何监听用户是否修改过信息
可以用 beforeRouteLeave 和 updated 来判断.export default { name: 'supplier', components:{cmtWrap,cmtContent ...
- 博弈论进阶之SG函数
SG函数 个人理解:SG函数是人们在研究博弈论的道路上迈出的重要一步,它把许多杂乱无章的博弈游戏通过某种规则结合在了一起,使得一类普遍的博弈问题得到了解决. 从SG函数开始,我们不再是单纯的同过找规律 ...
- Fragment与Activity的接口回调
这里说一个官方推荐的写法: private OnFragmentInteractionListener mListener; @Override public void onAttach(Contex ...
- ionic app打包和签名
ionic app打包和签名 1.首先在项目根目录执行 ionic platform add android 生成Android平台. 2.配置应用签名:在根目录下执行以下命令 keytool - ...
- Broadcast发送广播
一.知识介绍 1.[广播分类] ①有序广播:接收者A收到广播传递给B,B传给C,有序传递.任何一个环节都可以终止广播,也可以修改广播中携带的数据. 发送的方式:sendOrderedBroadcast ...
- python模块--zipfile文件压缩
zipfile模块是python中一个处理压缩文件的模块,解决了不少我们平常需要处理压缩文件的需求 ,本文主要谈谈zipfile几个常用的用法. 首先我在Windows操作系统中创建如下的文件目录: ...
- Windows Java包环境变量的设置
复制Bin文件所在路径 验证
- NSTimer 不工作 不调用方法
比如,定义一个NSTimer来隔一会调用某个方法,但这时你在拖动textVIew不放手,主线程就被占用了.timer的监听方法就不调用,直到你松手,这时把timer加到 runloop里,就相当于告诉 ...
- Svn在工作中的实践感悟
Svn是一款管理项目代码的版本控制系统,是基于集中式的版本控制系统.在工作中,由于实际开发工作的需要,部门是使用Svn来管理日常的项目开发任务.使用这么长时间了,来谈谈对Svn的感悟. 首先,说下工作 ...
- 英语口语练习系列-C18-Wildest Dreams
词汇复习 actor 演员 afternoon 下午 alive 活着的 apple 苹果 adjective 形容词 air 空气 animal 动物 April 四月 adult 成年人 airp ...