可视化学习 Go 并发编程

2017.8.5

黄庆兵 - #Gopher Meetup 杭州#

网易

bingohuang.com

并发

简单来说,并发是一种构造程序的方式

2

Concurrency is not Parallelism

1. 并发很强大
2. 并发帮助实现并行,使并行(扩展等)变得容易
3. 并发不是并行,并发重点是架构,并行重点是执行,两者不同,但相关。
3

可视化 并发(Concurrency) & 并行(Parallelism)

一图胜千言!

4

为什么要关注并发?当今是多核的时代,并发的世界

5

多核的时代

6

并发编程并不容易,但 Go 对并发有很好的支持

7

Go 语言中的并发

8

Goroutine

f("hello", "world") // f 运行; 等待
go f("hello", "world") // f 开始运行
g() // 不用等待 f 返回
9

Channel

timerChan := make(chan time.Time)
go func() {
    time.Sleep(deltaT)
    timerChan <- time.Now() // 将时间发送给timerChan
}()
// 做一些其它事情;准备接收
// 接收会阻塞,直到 timerChan 传送值
// 值的发送是另上个 goroutine 结束的时间
completedAt := <-timerChan
10

Select

select {
case v := <-ch1:
    fmt.Println("channel 1 sends", v)
case v := <-ch2:
    fmt.Println("channel 2 sends", v)
default: // 可选
    fmt.Println("neither channel was ready")
}
11

Go 让并发编程变的简单起来

12

但是问题来了

13

祭出法宝 - GoTrace

一种将 Go 并发过程可视化的开源工具

出自 divan 大神,主要包含两个程序:

感谢 divan 大神 提供了这款工具和不少 Go 并发模式的素材

14

说了这么多,耳听为虚,眼见为实

15

1. HELLO, WORLD!

package main

func main() {
  // 创建一个 int 类型的 channel
  ch := make(chan int)

  // 启动一个新的匿名 goroutine
  go func() {
      // 发送 42 给 channel
      ch <- 42
  }()
  // 从 channel 读取
  <-ch
}
16

2. 计时器

func tick(d time.Duration) <-chan int {
    c := make(chan int)
    go func() {
        time.Sleep(d)
        c <- 1
    }()
    return c
}

func main() {
    trace.Start(os.Stderr)
    for i := 0; i < 24; i++ {
        c := tick(100 * time.Millisecond)
        <-c
    }
    trace.Stop()
}
17

3. 乒乓球 - 2 个玩家

func main() {
    var Ball int
    table := make(chan int)

    // 2个玩家
    go player(table)
    go player(table)

    table <- Ball
    time.Sleep(1 * time.Second)
    <-table
}

func player(table chan int) {
    for {
        ball := <-table
        ball++
        time.Sleep(100 * time.Millisecond)
        table <- ball
    }
}
18

3. 乒乓球 - 2 个玩家

19

3. 乒乓球 - 3 个玩家

func main() {
    var Ball int
    table := make(chan int)

    // 3个玩家
    go player(table)
    go player(table)
    go player(table)

    table <- Ball
    time.Sleep(1 * time.Second)
    <-table
}
20

3. 乒乓球 - 36 个玩家

func main() {
    var Ball int
    table := make(chan int)

    // 36个玩家
    for i := 0; i < 36; i++ {
            go player(table)
    }

    table <- Ball
    time.Sleep(1 * time.Second)
    <-table
}
21

4. 素数筛-算法

感谢白明老师的动图

要找到小于或等于给定整数n的素数,可以使用Eratosthenes' sieve(埃拉托斯特尼)算法

算法核心思想:先用最小的素数2去筛,把2的倍数剔除掉;下一个未筛除的数就是素数(这里是3)。
再用这个素数3去筛,筛除掉3的倍数... 这样不断重复下去,直到筛完为止。
22

4. 素数筛-实现

func generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i // Send 'i' to channel 'ch'.
    }
}
func filter(src <-chan int, dst chan<- int, prime int) {
    for i := range src { // Loop over values received from 'src'.
        if i%prime != 0 {
            dst <- i // Send 'i' to channel 'dst'.
        }
    }
}
func main() {
    ch := make(chan int)
    go generate(ch)
    for i := 0; i < 10; i++ {
        prime := <-ch
        fmt.Println(prime)
        out := make(chan int)
        go filter(ch, out, prime)
        ch = out
    }
}
23

4. 素数筛-可视化

原型是 Daisy-Chain 模式,Rob Pike 在他 2012年 的 golang talks 中有提到

24

5. 其它-Goroutines 泄露

func leaker() {
    time.Sleep(1000000 * time.Minute)
}

func main() {
    trace.Start(os.Stderr)
    for i := 0; i < 1000; i++ {
        go leaker()
    }
    trace.Stop()
}
25

GoTrace 用法简介

Usage: gotrace [trace.out] or [main.go]
       (if you pass .go file to gotrace, it will modify code on the fly,
       adding tracing, run it and collect the trace automagically)
trace.Start(os.Stderr)
trace.Stop()
docker run --rm -it -e GOOS=darwin -v $(pwd):/src \
  hub.c.163.com/bingohuang/gotrace:go1.8.1 \
  go build -o /src/binary /src/main.go
./binary 2> ./trace.out
gotrace ./trace.out
26

使用场景

27

Thank you

2017.8.5

黄庆兵 - #Gopher Meetup 杭州#

网易

bingohuang.com

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)