1. 概念

单纯的将函数并发执行是没有意义的,函数与函数之间需要交换数据才能提现并发执行函数的意义
虽然可以使用共享内存来进行数据的交换,但是在共享内存在不同的goroutine中容易发生竟态问题,
为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题,

go语言的并发模型是CSP,提倡通过通信共享内存,而不是通过共享内存而实现通信

如果说goroutine是go程序并发的执行体,那么channel就是它们之间的连接,
channel是可以让一个goroutine发送一个特定值到另一个goroutine的通信机制

go语言中的通道(channel)是一种特殊的类型,通道像一个传送带、队列,总是遵循先进先出的原则,
保证收发数据的顺序,每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素的类型

通道类型的空值是nil,空接口也是nil
声明通道之后需要使用make函数初始化通道才能使用

通道有发送send,接收receive,关闭close三种操作
我们通过内置的close函数来关闭通道 close(chan)

关闭通道需要注意的事情:只有在通知接收方goroutine所有数据都发送完毕的时候才需要关闭通道,
通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的
而关闭通道是不必须的

关闭通道后有一下几点:
1. 对一个关闭的通道在发送值就会导致panic
2. 对一个关闭的通道进行接后,会一直接收值知道通道为空
3. 对于一个关闭的且没有值得通道进行接收,会返回对应类型的零值
4. 关闭一个已经关闭的通道会导致panic

使用无缓冲通道可以实现goroutine之间的同步,无缓冲通道又称为阻塞的通道

2. 无缓冲通道
 
package main

import (
"fmt"
"sync"
) func recv(c chan int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("接收成功", <-c)
} func main() {
var wg sync.WaitGroup
// 无缓冲通道实现goroutine之间的同步
ch1 := make(chan int)
wg.Add(1)
go recv(ch1, &wg) // 启动goroutine从通道接收值
ch1 <- 10 // 无缓冲通道因为没有人接收,会阻塞在这里形成死锁
fmt.Println("发送成功")
wg.Wait()
// 报错:fatal error: all goroutines are asleep - deadlock!
// 原因:无缓冲通道只有在有人接收值得时候才能发送值 /* 总结:
无缓冲通道的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,
两个goroutine将继续执行,相反,如果接收方先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值 使用无缓冲通道进行通信将导致发送和接收的goroutine同步化,因此无缓冲通道也被称为同步通道
*/
}

  

3. 有缓冲通道

package main

import "fmt"

func main() {
ch := make(chan int, 1) // 创建一个容量为1的有缓冲通道
ch <- 10
fmt.Println("发送成功") fmt.Println(len(ch), cap(ch)) /*
只要通道的容量大于0,该通道就是有缓冲通道
len函数可以获取通道内元素的个数,cap函数可以获取通道的容量
*/
}

  

4. 关闭通道 close()

package main

import "fmt"

func main() {
//可以通过内置的close函数关闭channel,(如果你的管道不往里存值或取值的时候一定要记得关闭管道)
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i
}
close(c)
// channel不需要通过close释放资源,只要没有goroutine持有channel,垃圾回收机制会回收掉该channel
}() /* 方法一
for{
if ret, ok := <-c; ok {
fmt.Println(ret)
} else {
break
}
}
*/
// 注意:遍历通道获取数据时,通道必须得关闭,否则遍历到最后就会陷入死锁
for ret := range c {
fmt.Println(ret)
} fmt.Println("main 结束了...")
}

  

5. 单向通道

package main

import "fmt"

func counter(c chan<- int) {
// 单向通道只能发送
for i := 0; i < 10; i++ {
c <- i
}
close(c)
}
func squarer(c1 <-chan int, c2 chan<- int) {
// 两个单向通道,c1只能接收,c2只能发送
for {
ret, ok := <-c1
if !ok {break}
c2 <- ret * ret
}
close(c2)
}
func printer(c <-chan int) {
// 单向通道只能接收
for v := range c {
fmt.Println(v)
}
} func main() {
// 单向通道:限制通道在函数中只能发送或只能接收
ch1 := make(chan int)
ch2 := make(chan int) go counter(ch1)
go squarer(ch1, ch2)
printer(ch2)
}

 1.chan<- int是一个只能发送的通道,可以发送但是不能接收;

2.<-chan int是一个只能接收的通道,可以接收但是不能发送。

在函数传参及任何赋值操作中将双向通道转换为单向通道是可以的,但反过来是不可以的。

6. 通道总结

channel常见的异常总结,如下图:

注意:关闭已经关闭的channel也会引发panic。

 

golang中的channel的更多相关文章

  1. 理解golang中的channel

    channel是goroutine之间的通信机制.可以类比线程间的通信,线程间的通信有多种方式,比如线程上下文.共享内存.IPC通信.socket实现不同机器间的通信. channel用起来很简单,绑 ...

  2. golang中并发sync和channel

    golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...

  3. golang中并发的相关知识

    golang中done channel理解:https://segmentfault.com/a/1190000006261218 golang并发模型之使用Context:https://segme ...

  4. golang 中 channel 的非阻塞访问方法

    在golang中,基本的channel读写操作都是阻塞的,如果你想要非阻塞的,可以使用如下示例: 即只要在select中加入default,阻塞立即变成非阻塞: package main import ...

  5. 说说不知道的Golang中参数传递

    本文由云+社区发表 导言 几乎每一个C++开发人员,都被面试过有关于函数参数是值传递还是引用传递的问题,其实不止于C++,任何一个语言中,我们都需要关心函数在参数传递时的行为.在golang中存在着m ...

  6. 【荐】详解 golang 中的 interface 和 nil

    golang 的 nil 在概念上和其它语言的 null.None.nil.NULL一样,都指代零值或空值.nil 是预先说明的标识符,也即通常意义上的关键字.在 golang 中,nil 只能赋值给 ...

  7. Golang 中的指针 - Pointer

    http://www.cnblogs.com/jasonxuli/p/6802289.html   Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int ...

  8. 进一步认识golang中的并发

    如果你成天与编程为伍,那么并发这个名词对你而言一定特别耳熟.需要并发的场景太多了,例如一个聊天程序,如果你想让这个聊天程序能够同时接收信息和发送信息,就一定会用到并发,无论那是什么样的并发. 并发的意 ...

  9. 六、golang中的结构体和方法、接口

    结构体: 1.用来自定义复杂数据结构 2.struct里面可以包含多个字段(属性) 3.struct类型可以定义方法,注意和函数的区分 4.strucr类型是值类型 5.struct类型可以嵌套 6. ...

随机推荐

  1. Linux宝塔面板部署运行jar包

    登录面板 安装插件 把jar包上传上去 设置jar包 填写项目启动的端口 然后点击确定 会自动启动 然后浏览器打开 ip:端口 即可

  2. 【LeetCode】951. Flip Equivalent Binary Trees 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 日期 题目地址:https://leetcod ...

  3. 1016 - Brush (II)

    1016 - Brush (II)    PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB Afte ...

  4. Amazing!!CSS 也能实现烟雾效果?

    最近利用 CSS 实现了一些看似超出 CSS 能力的效果: 巧用渐变实现高级感拉满的背景光动画 Amazing!!CSS 也能实现极光? 本文继续此系列,本文主要想探讨一下,使用 CSS 能否比较好的 ...

  5. mybatis 内部定义对象和集合

    mapper 配置文件中  引入两个重要的标签:association和collection标签.

  6. 第四十六个知识点 在Sigma协议中,正确性,公正性和零知识性意味着什么

    第四十六个知识点 在Sigma协议中,正确性,公正性和零知识性意味着什么 Sigma协议 Sigma协议是Alice想要向Bob证明一些东西的协议(Alice知道一些秘密).他们有下面的一般范式:Al ...

  7. 编写Java程序,在硬盘中选取一个 txt 文件,读取该文档的内容后,追加一段文字“[ 来自新华社 ]”,保存到一个新的 txt 文件内

    查看本章节 查看作业目录 需求说明: 在硬盘中选取一个 txt 文件,读取该文档的内容后,追加一段文字"[ 来自新华社 ]",保存到一个新的 txt 文件内 实现思路: 创建 Sa ...

  8. Java面向对象笔记 • 【第8章 内部类和泛型】

    全部章节   >>>> 本章目录 8.1 内部类 8.1.1 内部类概述 8.1.2 内部类使用 8.1.3 实践练习 8.2 静态内部类 8.2.1 静态内部类的实现 8.2 ...

  9. Kylin开启Kerberos安全认证

    Kylin开启Kerberos安全认证, 由于Kylin是依赖Hbase启动的, Kylin启动脚本kylin.sh中就是调用的Hbase的启动脚本, 所以当Hbase开启了Keberos之后就等于K ...

  10. openmesh - src - trimesh delete and add elements

    openmesh - src - trimesh delete and add elements openmesh 版本 8.1 About 本文主要介绍openmesh的如下接口 add_verte ...