Go并发模式(1):基础与并发调度

笔记内容来源于Google Go团队的两个Talk:

  1. Go Concurrency Patterns (YouTube Link)
  2. Advanced Go Concurrency Patterns (YouTube Link)

结合自己的一些理解写的笔记。

什么是并发?

并发是多个独立的计算流程组合进行的过程。并发正在影响软件开发过程,它应当是一种构建软件的途径,良好的语言与工具可以帮助我们比较容易地写出整洁的代码,与现实世界交互。

并发并不是并行

如果你只有一个CPU处理器,你也可以做到并发,在一段时间内做到做多件事情,但是不能称作并行。
并发更多地是指一种开发模式,一个良好设计的并发程序能够很好地在多核CPU甚至多个主机上分布式地工作。

并发:用于软件构建的model

一个良好的并发模型需要具备:

  • 易于理解
  • 易于使用
  • 易于推理、debug

写过Java或者类似传统的线程并发模型的都有体验,写并发程序的时候,Java能够提供的是比较基础的并发工具,并且大部分通过库的方式提供。你需要了解:Java内存模型,Java并发工具,锁,线程,Barrier,Semaphones...

Golang的并发起源于1978年Hoare的Communicating Sequential Process论文,后来多种语言发展了相似的并发特性,包括:Occam, Erlang, Newsqueak, Concurrent ML, Alef, Limbo等。Go与Newsqueak、Alef、Limbo相似,是这个分支的最新语言,特点是语言级别支持的channel。
而Erlang与初始的CSP的概念类似,通过name与process通信,但是模型是等价的。Erlang的process与Golang的channel的区别可以类比于通过文件写入文件与通过文件描述符(File Description)写入文件。

Golang的并发

Golang的并发由Goroutine和Channel两个重要的工具作为特征。
下面的程序可以非常方便地创建一个并发程序:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func boring(msg string, c chan string) {
    for i := 0; ; i++ {
        c <- fmt.Sprintf("%s %d", msg, i) // Expression to be sent can be any suitable value.
        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
    }
}

func main() {
    c := make(chan string)
    go boring("boring!", c)
    for i := 0; i < 5; i++ {
        fmt.Printf("You say: %q\n", <-c) // Receive expression is just a value.
    }
    fmt.Println("You're boring; I'm leaving.")
}

通过go关键字,我们创建了一个goroutine执行boring函数,同时传入了一个Channel c用于goroutine间的通信。执行main函数的goroutine从c接受消息,boring函数在另一个goroutine里发送信息。

默认的make(chan string)创造的channel是一个Buffer size为1的队列,当生产者通过c <-放入一个数据后,它会阻塞到另一个goroutine接收这个数据;同理,执行<- c的goroutine也会阻塞等待数据的放入后继续执行。
Channel的用途除了通讯更在于『同步』。

当然了,我们也可以创建一个buffer大于1的Channel,只有如果是chan <-时,在buffer满的时候会阻塞;如果是<- chan时,buffer为空时会阻塞。这样的channel相对较难debug。

Golang中意的方式是,不要通过共享内存的方式通信,而是通过通信的方式共享内存。(Don't communicate by sharing memory; Share memory by communicating.)

Go并发调度机制

本节内容参考文章Go Scheduler .

用户空间的线程和内核空间的线程之间的映射关系有多种:

  1. N:1 多个用户级别的线程都在一个内核线程上运行。context切换会很快,但无法利用多核。
  2. 1:1 一个用户线程只在一个内核线程上跑,这样context切换很慢。
  3. M:N 多个用户线程在多个内核线程上跑,调度难度很大。

Go的调度器包含三个结构:M(内核级线程);P(调度的上下文);G(Goroutine,有自己的栈,instruction pointer等其它信息)。

image

这里我们有两个内核级线程(M),每个M都有一个context P,也有一个正在运行的goroutine G。P的数量可以由GOMAXPROCS()设置,它代表了真正的并发度。
除了当前运行的goroutine,还有灰色的ready状态的goroutine等待被调度。P维护着这个队列,称为runqueue。
当执行 go func时,新的goroutine就被append到了runqueue里。当ontext运行到了调度的点后,会从runqueue pop一个goroutine,设置栈和instruction pointer,开始执行这个goroutine。
为了避免资源竞争,每个context P都有自己的run queue。为何要维护多个context P呢?是为了当一个OS线程M陷入阻塞时,P会从之前的M上脱离,转而在另一个M上运行。

image


当系统调用(syscall)结束时,线程必须从其它OS线程中偷一个context P。如果偷不到,就把当前的goroutine扔到global的runqueue里,而自己放回到线程缓存中。contexts也会周期性地检查global的run queue,否则这些goroutine会永远无法被执行。

当一个context P执行完毕了自己runqueue里全部goroutine,当各个context下的负债不均匀,Golang也会通过steal的方式调度。

What's Next

下一篇中,将介绍Golang团队分享中介绍的若干并发设计模式。

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

推荐阅读更多精彩内容

  • http://skoo.me/go/2013/11/29/golang-schedule?hmsr=studygo...
    baboon阅读 2,189评论 0 3
  • 轻量级进程模型: 用同步IO的方法写程序的逻辑,第二点是用尽可能多的并发进程来提升IO并发的能力。 核心思想,第...
    lifesoul阅读 2,630评论 4 1
  • Goroutine是Go里的一种轻量级线程——协程。相对线程,协程的优势就在于它非常轻量级,进行上下文切换的代价非...
    witchiman阅读 4,741评论 0 9
  • 今天介绍一下 go语言的并发机制以及它所使用的CSP并发模型 CSP并发模型 CSP模型是上个世纪七十年代提出的,...
    falm阅读 68,163评论 10 80
  • go语言的并发机制以及它所使用的CSP并发模型 CSP并发模型CSP模型是上个世纪七十年代提出的,用于描述两个独立...
    seven_son阅读 2,594评论 0 5