Go 并发编程可视化

2017.8.1

黄庆兵 - 网易

并发

是一种构造程序的方式

2

Concurrency is not Parallelism - 并发不是并行

1. 并发很强大
2. 并发帮助实现并行,使并行(扩展等)变得容易
3. 并发不是并行
3

并发(Concurrency) VS 并行(Parallelism)

并发:

- 将相互独立的执行过程综合到一起的编程技术。
- 并发是指同时处理(deal)很多事情。
- 既可以运行在单核,也可以运行在多核上

并行:

- 同时执行(通常是相关的)计算任务的编程技术。
- 并行是指同时能做(do)很多事情。
- 需要运行在多核上

总结:

- 两者不同,但相关,并发重点是架构,并行重点是执行。
- 并发提供了一种方式让我们能够设计一种方案将问题(非必须的)并行的解决。
4

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

5

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

6

软件不应该是这样子

7

而是这个趋势

8

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

9

Go 语言中的并发

10

Goroutine

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

Channel

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

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")
}
13

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

14

但是问题来了

15

祭出法宝 - GoTrace

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

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

16

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

17

1. HELLO, WORLD!

package main

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

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

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()
}
19

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
    }
}
20

3. 乒乓球 - 2 个玩家

21

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
}
22

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
}
23

4. 素数筛-算法

素数是一个自然数,它具有两个截然不同的自然数除数:1和它本身。 要找到小于或等于给定整数n的素数,我们可以使用Eratosthenes' sieve(埃拉托斯特尼)算法

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

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
    }
}
25

4. 素数筛-可视化

一图胜千言

26

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()
}
27

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)
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
28

场景

29

Thank you

2017.8.1

黄庆兵 - 网易

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.)