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 ...
随机推荐
- 在ubuntu16.04中初次体验.net core 2.0
.net core运行在Linux中的例子.文章已经很多了,看了一些之后也想体验一下,顺便记录一下…… 环境:win10 1709.它内置的Linux子系统(这里安装的是Ubuntu 16.04) 一 ...
- 【20190228】JavaScript-数组的操作
1. 创建数组 var array = new Array(); var array = new Array(5); var array = new Array(1,2,3,"a" ...
- 在IIS上部署你的ASP.NET Core 2.1项目
1.在控制面板→程序→启用或关闭Windows功能→勾选Internet Information Services以及Web管理工具下的IIS管理控制台 2.IIS 安装AspNetCoreModul ...
- 学习Python3基础知识过程中总结
print()中end==""的用法 例子:用Python3输出九九乘法表: for i in range(1,10): for j in range(1,i+1): s=i*j ...
- Android网页打开指定App
一.只打开APP操作 通过用手机的浏览器(内置,第三方都可)访问一个网页,实现点击一个链接启动自己的应用,并传递数据. 首先在Mainifest文件里面对要启动的Activity添加一个过滤器. 网页 ...
- android.database.sqlite.SQLiteException: no such column: aaa (code 1): , while compiling: DELETE FROM users WHERE user_name=aaa解决办法
在写安卓登录注册时注销按钮闪退发现: 这是因为此处错误: 因为用户名为字符串,不是整型,数据库查询要引号,少了引号查询不了,导致闪退 解决后成功运行 正确用法: 下次谨记,细节决定成败呀!
- windows下QT打包
1.找到对应的MinGW命令,打开 2.进入exe目录 3.执行windeployqt XX.exe
- ASP.NET Zero--前端应用程序
前端应用程序 ASP.NET Zero包含可以作为您的公共网站或应用程序着陆页的起点的前端页面.首次运行项目时,您会看到主页如下所示: 这里有两页:主页和关于.这些页面的内容只是占位符和演示目的.您可 ...
- 系统功能调用Windows操作系统原理实验
一.实验目的 1.熟悉操作系统的系统功能调用. 2.掌握用C语言实现系统功能调用的方法和步骤. 3.掌握利用10H号功能调用(BIOS的显示I/O功能调用)来实现对屏幕的操作与控制. 二.实验内容 1 ...
- LeetCode算法题-Next Greater Element I(Java实现)
这是悦乐书的第244次更新,第257篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第111题(顺位题号是496).你有两个数组(没有重复)nums1和nums2,其中nu ...