Go - chan 通道
概述
原来分享的基础语法的时候,还未分享过 chan 通道,这次把它补上。
chan 可以理解为队列,遵循先进先出的规则。
在说 chan 之前,咱们先说一下 go 关键字。
在 go 关键字后面加一个函数,就可以创建一个线程,函数可以为已经写好的函数,也可以是匿名函数。
举个例子:
func main() {
fmt.Println("main start")
go func() {
fmt.Println("goroutine")
}()
fmt.Println("main end")
}
输出:
main start
main end
为什么没有输出 goroutine ?
首先,我们清楚 Go 语言的线程是并发机制,不是并行机制。
那么,什么是并发,什么是并行?
并发是不同的代码块交替执行,也就是交替可以做不同的事情。
并行是不同的代码块同时执行,也就是同时可以做不同的事情。
举个生活化场景的例子:
你正在家看书,忽然电话来了,然后你接电话,通话完成后继续看书,这就是并发,看书和接电话交替做。
如果电话来了,你一边看书一遍接电话,这就是并行,看书和接电话一起做。
说回上面的例子,为什么没有输出 goroutine ?
main 函数是一个主线程,是因为主线程执行太快了,子线程还没来得及执行,所以看不到输出。
现在让主线程休眠 1 秒钟,再试试。
func main() {
fmt.Println("main start")
go func() {
fmt.Println("goroutine")
}()
time.Sleep(1 * time.Second)
fmt.Println("main end")
}
输出:
main start
goroutine
main end
这就对了。
接下来,看看如何使用 chan 。
声明 chan
// 声明不带缓冲的通道
ch1 := make(chan string)
// 声明带10个缓冲的通道
ch2 := make(chan string, 10)
// 声明只读通道
ch3 := make(<-chan string)
// 声明只写通道
ch4 := make(chan<- string)
注意:
不带缓冲的通道,进和出都会阻塞。
带缓冲的通道,进一次长度 +1,出一次长度 -1,如果长度等于缓冲长度时,再进就会阻塞。
写入 chan
ch1 := make(chan string, 10)
ch1 <- "a"
读取 chan
val, ok := <- ch1
// 或
val := <- ch1
关闭 chan
close(chan)
注意:
close 以后不能再写入,写入会出现 panic
重复 close 会出现 panic
只读的 chan 不能 close
close 以后还可以读取数据
示例
func main() {
fmt.Println("main start")
ch := make(chan string)
ch <- "a" // 入 chan
go func() {
val := <- ch // 出 chan
fmt.Println(val)
}()
fmt.Println("main end")
}
输出:
main start
fatal error: all goroutines are asleep - deadlock!
What ? 这是为啥,刚开始就出师不利呀?
因为,定义的是一个无缓冲的 chan,赋值后就陷入了阻塞。
怎么解决它?
声明一个有缓冲的 chan。
func main() {
fmt.Println("main start")
ch := make(chan string, 1)
ch <- "a" // 入 chan
go func() {
val := <- ch // 出 chan
fmt.Println(val)
}()
fmt.Println("main end")
}
输出:
main start
main end
为啥没有输出 a , 和前面一样,主线程执行太快了,加个休眠 1 秒钟,再试试。
func main() {
fmt.Println("main start")
ch := make(chan string, 1)
ch <- "a" // 入 chan
go func() {
val := <- ch // 出 chan
fmt.Println(val)
}()
time.Sleep(1 * time.Second)
fmt.Println("main end")
}
输出:
main start
a
main end
这就对了。
再看一个例子:
func main() {
fmt.Println("main start")
ch := make(chan string)
go func() {
ch <- "a" // 入 chan
}()
go func() {
val := <- ch // 出 chan
fmt.Println(val)
}()
time.Sleep(1 * time.Second)
fmt.Println("main end")
}
输出:
main start
a
main end
再看一个例子:
func producer(ch chan string) {
fmt.Println("producer start")
ch <- "a"
ch <- "b"
ch <- "c"
ch <- "d"
fmt.Println("producer end")
}
func main() {
fmt.Println("main start")
ch := make(chan string, 3)
go producer(ch)
time.Sleep(1 * time.Second)
fmt.Println("main end")
}
输出:
main start
producer start
main end
带缓冲的通道,如果长度等于缓冲长度时,再进就会阻塞。
再看一个例子:
func producer(ch chan string) {
fmt.Println("producer start")
ch <- "a"
ch <- "b"
ch <- "c"
ch <- "d"
fmt.Println("producer end")
}
func customer(ch chan string) {
for {
msg := <- ch
fmt.Println(msg)
}
}
func main() {
fmt.Println("main start")
ch := make(chan string, 3)
go producer(ch)
go customer(ch)
time.Sleep(1 * time.Second)
fmt.Println("main end")
}
输出:
main start
producer start
producer end
a
b
c
d
main end
Go - chan 通道的更多相关文章
- [系列] Go - chan 通道
目录 概述 声明 chan 写入 chan 读取 chan 关闭 chan 示例 推荐阅读 概述 原来分享基础语法的时候,还未分享过 chan 通道,这次把它补上. chan 可以理解为队列,遵循先进 ...
- 【练习】goroutine chan 通道 总结
1. fatal error: all goroutines are asleep - deadlock! 所有的协程都休眠了 - 死锁! package mainimport("fmt&q ...
- Go Example--关闭通道
package main import ( "fmt" ) func main() { jobs := make(chan int, 5) done := make(chan bo ...
- go 学习笔记---chan
如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制.一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 go ...
- Go语言基础之反射
Go语言基础之反射 本文介绍了Go语言反射的意义和基本使用. 变量的内在机制 Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息. 值信息:程序运行过程中可动态变化的. 反射介绍 反射是指 ...
- Go语言反射reflect
目录 通过反射获取类型信息 理解反射的类型(Type)与种类(Kind) reflect.Elem() - 通过反射获取指针指向的元素类型 通过反射获取结构体的成员类型 通过反射获取值信息 使用反射值 ...
- go语言程序设计学习笔记-1
https://www.jb51.net/article/126998.htm go标准库文档https://studygolang.com/pkgdoc 1. 如果想要再本地直接查看go官方文档,可 ...
- golang基础--类型与变量
基础知识--类型与变量 基本类型 布尔型:bool 长度: 1字节 取值范围: false, true 注意事项: 不可以使用数字代表,不像 python中可是使用 1和0表示 整型: int/uin ...
- Go第十篇之反射
反射是指在程序运行期对程序本身进行访问和修改的能力.程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分.在运行程序时,程序无法获取自身的信息. 支持反射的语言可以在程序编译期将变量 ...
随机推荐
- XMind破解版,2019年8月好使
越来越多的公司用思维导图了,进行编写测试用例,以下为破解版,亲身实验才发 的 ,中国时间2019年8月5日 下载安装包: 链接:https://pan.baidu.com/s/1-ubJLPSEpH ...
- postman强大的团队协作功能
今天无意在调项目的接口 ,使用的postman工具 ,自己写的接口信息,只能自己看 ,感觉有点不方便,如果一个公司多名测试,如果一个人写接口信息,大家都能用,就会很节约时间 所以团队协作的功能就诞生了 ...
- java进销存管理系统的设计与实现-springboot源码
开发环境: Windows操作系统 开发工具:MyEclipse/Eclipse + JDK+ Tomcat + MySQL 数据库 项目简介: 系统前段页面采用jsp + JavaScrip ...
- python中pip添加国内镜像源后显著加速下载
python中pip添加国内镜像源后显著加速下载 更换pip源到国内镜像,很多国外的库下载非常慢,添加国内镜像后安装下载速度提升非常明显(亲测有些可以由几十kb加速到几MB) pip国内的一些镜像阿里 ...
- Vue.js 源码分析(七) 基础篇 侦听器 watch属性详解
先来看看官网的介绍: 官网介绍的很好理解了,也就是监听一个数据的变化,当该数据变化时执行我们的watch方法,watch选项是一个对象,键为需要观察的数据名,值为一个表达式(函数),还可以是一个对象, ...
- Jsp调用淘宝IP地址库获取来访IP详细信息
Jsp调用淘宝IP地址库获取来访IP详细信息 示例网页点击:www.trembler.cn/ipinfo/ipinfo(服务器有其他用处,页面已失效) String ip = request.ge ...
- Hibernate基于注解实现自关联树形结构实现
很久没用过Hibernate了,项目需求需要使用,并建立树形结构,在开发中遇到一些问题,在这里记录一下. 1.创建数据库表,主要是设置标志信息,不然插入数据库会报id不能插入null的错误. 2.创建 ...
- tornado的请求与响应
tornado请求与响应相关 一.配置文件config.py 中的settings 有哪些配置: debug:设置tornado是否工作再调试模式下,默认为false 即工作再生产模式下 true的特 ...
- 算法学习day01 栈和队列
1,设计一个算法利用顺序栈的基本运算判断一个字符串是否是回文 解题思路: 由于回文是从前到后和从后到前读都是一样的,所以只要将待判断的字符串颠倒 然后与原字符串相比较,就可以决定是否是回文了 ...
- nyoj 1278G: Prototypes analyze 与 二叉排序树(BST)模板
参考博客:https://blog.csdn.net/stpeace/article/details/9067029 参考博客:https://blog.csdn.net/baidu_35643793 ...