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的更多相关文章

  1. c#实现golang 的channel

    使用.NET的 BlockingCollection<T>来包装一个ConcurrentQueue<T>来实现golang的channel. 代码如下: public clas ...

  2. golang的channel实现

    golang的channel实现位于src/runtime/chan.go文件.golang中的channel对应的结构是: // Invariants: // At least one of c.s ...

  3. 【GoLang】golang context channel 详解

    代码示例: package main import ( "fmt" "time" "golang.org/x/net/context" ) ...

  4. 深入学习golang(2)—channel

    Channel 1. 概述 “网络,并发”是Go语言的两大feature.Go语言号称“互联网的C语言”,与使用传统的C语言相比,写一个Server所使用的代码更少,也更简单.写一个Server除了网 ...

  5. golang中channel的超时处理

    并发中超时处理是必不可少的,golang没有提供直接的超时处理机制,但可以利用select机制来解决超时问题. func timeoutFunc() { //首先,实现并执行一个匿名的超时等待函数 t ...

  6. 如何优雅的关闭golang的channel

    How to Gracefully Close Channels,这篇博客讲了如何优雅的关闭channel的技巧,好好研读,收获良多. 众所周知,在golang中,关闭或者向已关闭的channel发送 ...

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

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

  8. golang查看channel缓冲区的长度

    golang提供内建函数cap用于查看channel缓冲区长度. cap的定义如下: func cap(v Type) int The cap built-in function returns th ...

  9. Golang 入门 : channel(通道)

    笔者在<Golang 入门 : 竞争条件>一文中介绍了 Golang 并发编程中需要面对的竞争条件.本文我们就介绍如何使用 Golang 提供的 channel(通道) 消除竞争条件. C ...

随机推荐

  1. 【MySQL故障处理】 Seconds_Behind_Master= NULL Error_code: 1197

    版本:mysql 5.6.32**错误描述:**```Error_code: 1197Last_Error: Worker 3 failed executing transaction '352aa3 ...

  2. 深入理解Java内部类

         内部类就是定义在一个类中的另外一个类,是一种从属关系.在没有实际了解内部类之前,我始终困惑,为什么要在一个类中定义另外一个类,这不是增加代码结构复杂度么?现在才大致能知道这种设计的优势是大于 ...

  3. hdu_5810:Balls and Boxes(期望)

    这题似乎就是纯概率论.. E(V)=D(X_i)=npq (p=1/m,p+q=1) #include<bits/stdc++.h> using namespace std; typede ...

  4. 开始编写寄几的 CSS 基础库

    前言 在现在的互联网业务中,前端开发人员往往需要支持比较多的项目数量.很多公司只有 1-2 名前端开发人员,这其中还不乏规模比较大的公司.这时前端同学就需要独挡一面支持整个公司上下的前端业务,项目如流 ...

  5. iOS开发实战-上架AppStore 通过内购和广告获得收益

    写在前面 由于一些原因需要离职,准备重回大上海 忽然发现手头上也没什么独立App,那就随便写个放到AppStore上吧,凑个数吧.哈哈哈. 这个App是无聊找配色的时候看到的一套图 正好春节在家没什么 ...

  6. .editorconfig

    摘要 是什么 EditorConfig是一套用于统一代码格式的解决方案,可以帮助开发者在不同的编辑器和IDE之间定义和维护一致的代码风格. EditorConfig包含一个用于定义代码格式的文件和一批 ...

  7. tar命令(转)

    把常用的tar解压命令总结下,当作备忘: tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个, ...

  8. JavaSE中线程与并行API框架学习笔记——线程为什么会不安全?

    前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事 ...

  9. 基于vue的颜色选择器color-picker

    项目中有用到颜色选择器的童鞋们可以看过来了 关于color-picker的jquery的插件是有蛮多,不过vue组件没有吧,反正我没有找到, 虽然element-ui里面有这个,但是你愿意为了一个小功 ...

  10. webpack命令行

    前面的话 webpack提供了命令行接口(CLI),以便对构建过程进行配置和交互.这对于制定早期原型.轮廓.编写 npm 脚本 或者一些个人自定义需求很有用.本文将详细介绍webpack的命令行接口 ...