golang的Channel
golang的Channel
Channel 是 golang 一个非常重要的概念,如果你是刚开始使用 golang 的开发者,你可能还没有真正接触这一概念,本篇我们将分析 golang 的Channel
1. 引入
要讲 Channel 就避不开 Goroutine -- 协程。闲话不说, 直接上个例子
Goroutine demo:
package main
import (
"fmt"
"time"
)
func main() {
origin := 1
go doSomeCompute(origin)
time.Sleep(5 * time.Second)
}
func doSomeCompute(num int) {
result := num * 2
fmt.Println(result)
return
}
简单来说,例子中有一个 main 的协程,一个 doSomeCompute 的协程。还有个延时退出的方法等待计算协程计算结果。我们尝试思考这个例子的一些问题:
a. 如何获取 doSomeCompute 的计算结果?
b. 如何获取 doSomeCompute 的执行状态,当前是执行完了还是执行中?
如何解决这种问题呢?
Channel!
2. Channel
Channel怎么处理上面的问题?我们直接上代码:
举例
package main
import (
"fmt"
)
func main() {
origin := 1
//一个无缓冲Channel
res := make(chan int)
go doSomeCompute(origin, res)
fmt.Println(<-res)
}
func doSomeCompute(num int, res chan int) {
result := num * 2
res <- result
}
例子中,Channel 充当起了协程间通信的桥梁。Channel 可以传递到协程说明它是线程安全的,事实也是如此。Channel 可以理解为管道,协程 doSomeCompute 向 Channel 写入结果, main 中读取结果。注意,例子中读取 Channel 的地方会阻塞直到拿到计算结果,这样就解决了问题 a 和 b。
2. Channel 的方向性
上面的例子中,计算协程是负责计算并将计算结果写入 Channel ,如果我们希望保证计算协程不会从 Channel 中读取数据该怎么处理?很简单,看例子:
func doSomeCompute(num int, res chan<- int) {
result := num * 2
res <- result
}
这个参数的声明 chan<- int 就表示该函数只能讲数据写入 Channel ,而不能从中读取数据。后面的 int 表示 Channel 中数据的格式。同样的, 只可以读取数据的 Channel 可以声明为 <-chan int 。而例子中不带有方向声明的 Channel 则既可以写入也可以读取。
3. 阻塞性质
Channel 的读取和写入操作在各自的协程内部都是阻塞的。比如例子中 fmt.Println(<-res) , 这一语句会阻塞直至计算协程将计算结果放入,可以读出。也就是说,协程会阻塞直至从 res 中读出数据。
注意,无缓冲的 Channel 的读写都是阻塞的,有缓冲的 Channel 可以一直向里面写数据,直到缓存满才会阻塞。读取数据同理, 直至 Channel 为空才阻塞。
用一个典型的例子来说明缓冲和非缓冲 Channel 的区别:
package main
import "fmt"
func doSomeCompute(ch chan int) {
fmt.Println("deadlock test")
<- ch
}
func main() {
ch := make(chan int)
ch <- 1
go doSomeCompute(ch)
}
例子中,main 协程会向 ch 写入数据, 这一过程是阻塞的,也就是说,doSomeCompute 协程无法执行,程序死锁。输出如下:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
C:/mygo/src/demo/blog.go:12 +0x73
exit status 2
如果改成有缓冲的 Channel :
package main
import (
"fmt"
"time"
)
func doSomeCompute(ch chan int) {
fmt.Println("deadlock test")
<- ch
}
func main() {
ch := make(chan int, 1)
ch <- 1
go doSomeCompute(ch)
time.Sleep(1 * time.Second)
}
有与有缓冲的 Channel 写入后不阻塞(下一次写入才会阻塞),程序会继续执行。
4. Channel 的数据结构
Channel 在 golang 中实际上就是个数据结构。在 Golang 源码中, Channel 的数据结构 Hchan 的定义如下:
struct Hchan
{
uint32 qcount; // total data in the q
uint32 dataqsiz; // size of the circular q
uint16 elemsize;
bool closed;
uint8 elemalign;
Alg* elemalg; // interface for element type
uint32 sendx; // send index
uint32 recvx; // receive index
WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters
Lock;
};
时间仓促,这次对 Channel 介绍写的有点简单粗暴,下次再写。
golang的Channel的更多相关文章
- c#实现golang 的channel
使用.NET的 BlockingCollection<T>来包装一个ConcurrentQueue<T>来实现golang的channel. 代码如下: public clas ...
- golang的channel实现
golang的channel实现位于src/runtime/chan.go文件.golang中的channel对应的结构是: // Invariants: // At least one of c.s ...
- 【GoLang】golang context channel 详解
代码示例: package main import ( "fmt" "time" "golang.org/x/net/context" ) ...
- 深入学习golang(2)—channel
Channel 1. 概述 “网络,并发”是Go语言的两大feature.Go语言号称“互联网的C语言”,与使用传统的C语言相比,写一个Server所使用的代码更少,也更简单.写一个Server除了网 ...
- golang中channel的超时处理
并发中超时处理是必不可少的,golang没有提供直接的超时处理机制,但可以利用select机制来解决超时问题. func timeoutFunc() { //首先,实现并执行一个匿名的超时等待函数 t ...
- 如何优雅的关闭golang的channel
How to Gracefully Close Channels,这篇博客讲了如何优雅的关闭channel的技巧,好好研读,收获良多. 众所周知,在golang中,关闭或者向已关闭的channel发送 ...
- golang 中 channel 的非阻塞访问方法
在golang中,基本的channel读写操作都是阻塞的,如果你想要非阻塞的,可以使用如下示例: 即只要在select中加入default,阻塞立即变成非阻塞: package main import ...
- golang查看channel缓冲区的长度
golang提供内建函数cap用于查看channel缓冲区长度. cap的定义如下: func cap(v Type) int The cap built-in function returns th ...
- Golang 入门 : channel(通道)
笔者在<Golang 入门 : 竞争条件>一文中介绍了 Golang 并发编程中需要面对的竞争条件.本文我们就介绍如何使用 Golang 提供的 channel(通道) 消除竞争条件. C ...
随机推荐
- jQuery知识点整合
一.jQuery介绍 jQuery是一个函数库,一个js文件,页面用script标签引入js文件就可以使用 <script type="text/javascript" sr ...
- 第2章 rsync(一):基本命令和用法
本文目录: 2.1 说在前面的话 2.2 rsync同步基本说明 2.3 rsync三种工作方式 2.4 选项说明和示例 2.4.1 基础示例 2.4.2 "--exclude"排 ...
- C#中的Infinity和NaN
C#中double和float类型有两个特殊值: Infinity(无穷大):5.0 / 0.0 = Infinity NaN(not a number):0.0 / 0.0 = NaN 计算表达式 ...
- 开源个.NetCore写的 - 并发请求工具PressureTool
本篇和大家分享的是一个 并发请求工具,并发往往代表的就是压力,对于一些订单量比较多的公司这种情况很普遍,也因此出现了很多应对并发的解决方案如:分布式,队列,数据库锁等: 对于没有遇到过或者不可能线上来 ...
- java集合练习
分组练习: List<List<Student>> list=new ArrayList<List<Student>>();这个是二维集合,分组的时候 ...
- Bean 的生命周期 之 后处理Bean
这里先把Bean 的生命周期总结一下,然后引出后处理Bean 首先,Bean 的生命周期总共有11步: 1.instantiate bean对象实例化 2.populate properties 封装 ...
- vue-项目入门
初入前端的新人在碰到vue.js后,去过官网,估计粗略的看下api文档以后会以为vue的安装只是把那串js代码直接粘贴复制到文档即可,虽然这样是可以,但那在项目中并不合适. 项目中的vue引入(配制安 ...
- 整理自己的.net工具库
前言 今天我会把自己平日整理的工具库给开放出来,提供给有需要的朋友,如果有朋友平常也在积累欢迎提意见,我会乐意采纳并补充完整.按照惯例在文章结尾给出地址^_^. 之前我开放其他源码的时候(Framew ...
- Spring源码解析——如何阅读源码
最近没什么实质性的工作,正好有点时间,就想学学别人的代码.也看过一点源码,算是有了点阅读的经验,于是下定决心看下spring这种大型的项目的源码,学学它的设计思想. 手码不易,转载请注明:xingoo ...
- Restful接口调用方法超详细总结
由于在实际项目中碰到的restful服务,参数都以json为准.这里我获取的接口和传入的参数都是json字符串类型.发布restful服务可参照文章http://www.cnblogs.com/jav ...