[系列] 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
就到这吧。
推荐阅读
gRPC
Gin 框架
基础篇
本文欢迎转发,转发请注明作者和出处,谢谢!
[系列] Go - chan 通道的更多相关文章
- Go - chan 通道
概述 原来分享的基础语法的时候,还未分享过 chan 通道,这次把它补上. chan 可以理解为队列,遵循先进先出的规则. 在说 chan 之前,咱们先说一下 go 关键字. 在 go 关键字后面加一 ...
- 转:Java NIO系列教程(五) 通道之间的数据传输
在Java NIO中,如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel(译者注:channel中文常译作通道)传输到另外一个channel. transferFro ...
- 【练习】goroutine chan 通道 总结
1. fatal error: all goroutines are asleep - deadlock! 所有的协程都休眠了 - 死锁! package mainimport("fmt&q ...
- nio系列(一)---nio重要组成
nio重要组成部分 前言:通过本文可以了解nio的重要组成部分,了解完基础的内容后后面理解才会简单一点.下一篇会讲讲nio的应用和io的对比.如果有不正确的地方还望指正. channel chanel ...
- Go Example--关闭通道
package main import ( "fmt" ) func main() { jobs := make(chan int, 5) done := make(chan bo ...
- [系列] Go gRPC Hello World
目录 概述 四类服务方法 安装 写个 Hello World 服务 推荐阅读 概述 开始 gRPC 了,这篇文章学习使用 gRPC,输出一个 Hello World. 用 Go 实现 gRPC 的服务 ...
- go 学习笔记---chan
如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制.一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 go ...
- Go语言系列之反射
变量的内在机制 Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息. 值信息:程序运行过程中可动态变化的. 反射介绍 反射是指在程序运行期对程序本身进行访问和修改的能力.程序在编译时,变量 ...
- Gin框架 - 自定义错误处理
目录 概述 错误处理 自定义错误处理 panic 和 recover 推荐阅读 概述 很多读者在后台向我要 Gin 框架实战系列的 Demo 源码,在这里再说明一下,源码我都更新到 GitHub 上, ...
随机推荐
- Android开发需要了解的 IM 知识
引言 即便在通讯如此发达的今天,IM 也依然是诸多场景下非常重要的基础能力.因此做为 一名 Android 开发,不可避免的会遇到一些IM 相关的需求或问题.本文以一个Android开发的角度来讲述I ...
- 对于BIO/NIO/AIO,你还只停留在烧开水的水平吗?
1.发发牢骚 相信大家在网上看过不少讲解 BIO/NIO/AIO 的文章,文章中举起栗子来更是夯吃夯吃一大堆,我是越看越觉得 What are you 你讲啥嘞? 本文将针对 BIO/NIO/AIO ...
- 16 | 脑洞大开:GUI测试还能这么玩(Page Code Gen + Data Gen + Headless)?
- 个人亲测,在win10系统下安装多实例mysql8.0详细教程
由于公司的新项目需要导入sql脚本,需要更高版本的mysql数据库,原来的数据库我也不想删除和升级,因此安装了第二个mysql8的实例,废话不多说,步骤如下: 1.下载mysqlGPL版本,我下载的版 ...
- C语言实现—学生成绩管理系统(Linux下运行)
开发环境:Linux 开发语言:C 相关文件下载: 码云:https://gitee.com/ikaros-521/c_project/tree/master/%E5%AD%A6%E7%94%9F%E ...
- 浅入深出Vue:发布项目
项目完成之后,当然不能满足于在我们的开发环境下跑一跑.我们可以打包发布到服务器上,让大家一起来欣赏一下你的作品. 那么 vue 项目如何打包发布呢,新建的项目目录下通常都有一个 README.md 的 ...
- Socket编程:listen()函数英文翻译
作者:C语言达人 链接:https://zhuanlan.zhihu.com/p/24951131 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 本篇翻译的list ...
- [Microsoft][ODBC Driver 11 for SQL Server][SQL Server]列名 'user1' 无效
唉,还是自己对php执行sql语句运用不熟练.... 我的错误代码是这样的,(解决办法在最后) $re=sqlsrv_query($conn, "select * from visitor ...
- [Spring+SpringMVC+Mybatis]框架学习笔记(六):事务
第7讲 事务 7.1 事务的概念 事务是一系列作为一个逻辑单元来执行的操作集合. 它是数据库维护数据一致性的单位,它讲数据库从一个一致状态,转变为新的另外一个一致状态.说的简单一点就是:如果一组处理步 ...
- 【DFS例题】等式
题目如下: 这道题依然是一道dfs(要求输出方案数很明显用dfs呐) 首先一个模板贴上来: void dfs()//参数用来表示状态 { if(到达终点状态) { ...//根据题意添加 return ...