Golang 之channel

1.channel 的特性

  • goroutine-safe,多个 goroutine 可以同时访问一个 channel。
  • 多goroutine共享和通信
  • 先进先出FIFO
  • 可以导致 goroutine 的 block 和 unblock

2.channel 的结构

image.png
type hchan struct {
    qcount   uint           // total data in the queue
    dataqsiz uint           // size of the circular queue
    buf      unsafe.Pointer // points to an array of dataqsiz elements 指向一个环形队列
    elemsize uint16
    closed   uint32
    elemtype *_type // element type
    sendx    uint   // send index
    recvx    uint   // receive index
    recvq    waitq  // list of recv waiters
    sendq    waitq  // list of send waiters

    // lock protects all fields in hchan, as well as several
    // fields in sudogs blocked on this channel.
    //
    // Do not change another G's status while holding this lock
    // (in particular, do not ready a G), as this can deadlock
    // with stack shrinking.
    lock mutex
}

3.channel的发送和接收

image.png
  • G1是发送者(写入),G2是接收者(读取)
  • G1首先获得锁向taskCh发送task,将task放入环形队列进行排队
  • G2获取锁并从队列中拿到task,此处的task是内存的一个拷贝
  • 拷贝是安全的,因为channel通过mutex得到保护,没有共享内容,所有的内容都是拷贝的。

3.channel实现阻塞和非阻塞

如果一直往channel中发送task,那么当channel满时,就会导致发送者(goroutine )的执行暂停。暂停的过程如下:

  • 暂停发生在调度时
  • Goroutines 是用户态的线程(协程),是由runtime管理其生命周期,包括创建和管理。而不是操作系统,与操作系统层面对线程的调度开销相比,Goroutines 是属于上层调度更为轻量级
  • Go的调度器是M:N的调度模型,可以通过三层结构来描述。其中M代表OS的线程,N代表goroutine ,P代表调度的上下文

一个线程持有一个P,P持有执行队列

goroutine 阻塞,但是对应的OS的Thread不会阻塞,同时一个Thread管理的一组goroutine 不会引起线程的上下文切换
如何恢复G1的运行,但是其他的goroutine 一旦开始接收channel中的数据时(channel就不满了),此时需要恢复channel的发送者goroutine

4.goroutine 的暂停

pause.png

goroutine的暂停(例如上述的阻塞),chan会通知调度器来暂存goroutine,并将其状态从运行态修改成等待状态,同时从p中调度新的goroutine。

  • 这一点是很有优势的,一方面我们没有销毁线程,而是通过上下文切换来调度新的goroutine,注意此处的上下文不是线程的,而是goroutine级别的。这个代价会很小。
  • 一旦channel 不在满的时候,暂停的goroutine将会被恢复。

5.goroutine 的恢复

resume.png
  • 等待状态的goroutine 的结构中有一个指针指向等待的元素
  • 发送者(G1)在调用调度器之前,会给自己创建一个sudoG,用于在将来被恢复或者唤醒。
  • 当channel 不再满的时候,接收者(G2)会弹出sudoG,此时G1的状态将变为可执行状态,并由调度器再次调度(但不是立马)

6.直接发送

direct.png

当G1需要被恢复时,从理论上说,需要获取chan的锁,但是runtime此处有个优雅的设计,使其代价更小。runtime直接把G1复制到接收队列G2的栈中,不需要获取chan的锁。也不需要从chan中进行内存的拷贝。

7.总结

  • 只有有缓冲区的chan,len方法才有效。实际上无缓冲的chan可以看成缓冲区为1的chan。chan没有可写的位置会阻塞。

  • chan双向和写入方向可以关闭,单向读不可以关闭,或者没有关闭操作

  • 如果chan有数据,进行关闭会包panic

  • chan使用场景
    双向chan:
    做缓冲,同步消息,并发控制,限流控制,锁控制,
    多层chan可以实现复杂业务,比如控制下层chan的close操作

单向chan,约束方法参数:
例如在接受方的chan设置成只读,可以约束chan无法关闭

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容