概述

原来分享的基础语法的时候,还未分享过 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 通道的更多相关文章

  1. [系列] Go - chan 通道

    目录 概述 声明 chan 写入 chan 读取 chan 关闭 chan 示例 推荐阅读 概述 原来分享基础语法的时候,还未分享过 chan 通道,这次把它补上. chan 可以理解为队列,遵循先进 ...

  2. 【练习】goroutine chan 通道 总结

    1. fatal error: all goroutines are asleep - deadlock! 所有的协程都休眠了 - 死锁! package mainimport("fmt&q ...

  3. Go Example--关闭通道

    package main import ( "fmt" ) func main() { jobs := make(chan int, 5) done := make(chan bo ...

  4. go 学习笔记---chan

    如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制.一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 go ...

  5. Go语言基础之反射

    Go语言基础之反射 本文介绍了Go语言反射的意义和基本使用. 变量的内在机制 Go语言中的变量是分为两部分的: 类型信息:预先定义好的元信息. 值信息:程序运行过程中可动态变化的. 反射介绍 反射是指 ...

  6. Go语言反射reflect

    目录 通过反射获取类型信息 理解反射的类型(Type)与种类(Kind) reflect.Elem() - 通过反射获取指针指向的元素类型 通过反射获取结构体的成员类型 通过反射获取值信息 使用反射值 ...

  7. go语言程序设计学习笔记-1

    https://www.jb51.net/article/126998.htm go标准库文档https://studygolang.com/pkgdoc 1. 如果想要再本地直接查看go官方文档,可 ...

  8. golang基础--类型与变量

    基础知识--类型与变量 基本类型 布尔型:bool 长度: 1字节 取值范围: false, true 注意事项: 不可以使用数字代表,不像 python中可是使用 1和0表示 整型: int/uin ...

  9. Go第十篇之反射

    反射是指在程序运行期对程序本身进行访问和修改的能力.程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分.在运行程序时,程序无法获取自身的信息. 支持反射的语言可以在程序编译期将变量 ...

随机推荐

  1. 基于SqlClient开发SQLServer工具类 伸手党的福音

    注意:代码直接Copy调用即可,SQLServer工具类跟我上一个Oracle工具类有所不同,区别在于调用存储过程中时参数的使用,无需输入对应的存储游标名称 特点:根据用户传入的参数类型来识别存储中对 ...

  2. .NET 跨域问题解决

    后端处理:var callback=context.Request.QueryString["callback"].ToString(); context.Response.Wri ...

  3. background 设置文本框背景图

    background 属性的作用是给元素设置背景,它是一个复合属性,常用的子属性如下: background-color 指定元素的背景颜色. background-image 指定元素的背景图像. ...

  4. C 函数声明、函数参数

    参考连接:https://www.runoob.com/cprogramming/c-functions.html 局部变量与全局变量在内存中的储存方式 全局变量保存在内存中的全局储存区中,占用静态的 ...

  5. js、jquery、css属性及出错集合

    *)注意使用jquery设置css的语法 css("propertyname","value");#单个时时逗号 css({"propertyname ...

  6. Linux入门——初识Linux

    Linux入门——初识Linux 摘要:本文主要说明了Linux是什么,Linux发展历史,以及同Linux系统有关的一些基本知识. 简介 操作系统 Linux系统同Windows系统.Mac系统一样 ...

  7. 妹纸对网易严选的Bra是什么评价?

    声明:这是一篇超级严肃的技术文章,请本着学习交流的态度阅读,谢谢! 一.网易商品评论爬取 1.评论分析 进入到网易严选官网,搜索“文胸”后,先随便点进一个商品. 在商品页面,打开 Chrome 的控制 ...

  8. [20190509]rman备份的疑问5.txt

    [20190509]rman备份的疑问5.txt --//别人跟我提到的rman备份问题,我开始以为是assm与mssm的问题,实际测试情况不是.--//开始备份时生成的备份集文件很大,以后会回缩(对 ...

  9. Hibernate与Mybatis 对比

    见知乎:https://www.zhihu.com/question/21104468 总结: 1:业务简单,不涉及多表关联查询的,用Hibernate更快,但是当业务量上去后,需要精通Hiberna ...

  10. 005-OpenStack-网络服务

    OpenStack-网络服务 [基于此文章的环境]点我快速打开文章 1.控制节点(controller) 1.1 创库授权 neutron mysql CREATE DATABASE neutron; ...