Go并发编程小测验: 你能答对几道题?

以下是10个Go并发编程的小测验,看看你能答对几道题?

1 Mutex

package main

import (
    "fmt"
    "sync"
)

var mu sync.Mutex
var chain string

func main() {
    chain = "main"
    A()
    fmt.Println(chain)
}

func A() {
    mu.Lock()
    defer mu.Lock()
    chain = chain + " --> A"
    B()
}

func B() {
    chain = chain + " --> B"
    C()
}

func C() {
    mu.Lock()
    defer mu.Lock()
    chain = chain + " --> C"
}
main --> A --> B --> C
main

2 RWMutex

package main

import (
    "fmt"
    "sync"
    "time"
)

var mu sync.RWMutex
var count int

func main() {
    go A()
    time.Sleep(2 * time.Second)
    mu.Lock()
    defer mu.Unlock()
    count++
    fmt.Println(count)
}

func A() {
    mu.RLock()
    defer mu.RUnlock()
    B()
}

func B() {
    time.Sleep(5 * time.Second)
    C()
}

func C() {
    mu.RLock()
    defer mu.RUnlock()
}

3 Waitgroup

package main

import (
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        time.Sleep(time.Millisecond)
        wg.Done()
        wg.Add(1)
    }()
    wg.Wait()
}
  • A: 不能编译
  • B: 无输出,正常退出
  • C: 程序hang住
  • D: panic

4 双检查实现单例

package doublecheck

import (
    "sync"
)

type Once struct {
    m    sync.Mutex
    done uint32
}

func (o *Once) Do(f func()) {
    if o.done ==1 {
        return
    }

    o.m.Lock()
    defer o.m.Unlock()
    if o.done ==0 {
        o.done =1
        f()
    }
}
  • A: 不能编译
  • B: 可以编译,正确实现了单例
  • C: 可以编译,有并发问题,f函数可能会被执行多次
  • D: 可以编译,但是程序运行会panic

5 Mutex

package main

import (
    "fmt"
    "sync"
)

type MyMutex struct {
    count int
    sync.Mutex
}

func main() {
    var mu MyMutex

    mu.Lock()
    var mu2 = mu
    mu.count++
    mu.Unlock()

    mu2.Lock()
    mu2.count++
    mu2.Unlock()

    fmt.Println(mu.count, mu2.count)
}
1, 1
1, 2

6 Pool

package main

import (
    "bytes"
    "fmt"
    "runtime"
    "sync"
    "time"
)

var pool = sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}

func main() {
    go func() {
        for {
            processRequest(1 <<28) // 256MiB
        }
    }()
    for i :=0; i <1000; i++ {
        go func() {
            for {
                processRequest(1 <<10) // 1KiB
            }
        }()
    }

    var stats runtime.MemStats
    for i :=0; ; i++ {
        runtime.ReadMemStats(&stats)
        fmt.Printf("Cycle %d: %dB\n", i, stats.Alloc)
        time.Sleep(time.Second)
        runtime.GC()
    }
}

func processRequest(size int) {
    b := pool.Get().(*bytes.Buffer)
    time.Sleep(500 * time.Millisecond)
    b.Grow(size)
    pool.Put(b)
    time.Sleep(1 * time.Millisecond)
}
  • A: 不能编译
  • B: 可以编译,运行时正常,内存稳定
  • C: 可以编译,运行时内存可能暴涨
  • D: 可以编译,运行时内存先暴涨,但是过一会会回收掉

7 channel

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    var ch chan int
    go func() {
        ch = make(chan int,1)
        ch <-1
    }()

    go func(ch chan int) {
        time.Sleep(time.Second)
        <-ch
    }(ch)

    c := time.Tick(1 * time.Second)
    for range c {
        fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
    }
}
#goroutines: 1
#goroutines: 2

8 channel

package main

import "fmt"

func main() {
    var ch chan int
    var count int

    go func() {
        ch <-1
    }()

    go func() {
        count++
        close(ch)
    }()

    <-ch

    fmt.Println(count)
}

9 Map

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    m.LoadOrStore("a",1)
    m.Delete("a")

    fmt.Println(m.Len())
}

10 happens before

package main

var c = make(chan int)
var a int

func f() {
    a =1
    <-c
}
func main() {
    go f()
    c <-0
    print(a)
}