专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Go并发编程小测验答案解析

题目 colobu.com/2019/04/28/…

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.Unlock()
    chain = chain + " --> A"
    B()
}

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

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

fatal error: all goroutines are asleep – deadlock! 引发panic mu未解锁又上锁引发死锁

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

fatal error: all goroutines are asleep – deadlock! 引发panic mu未解锁又上锁引发死锁

因为go为了防止写锁饥饿,当有写锁等待时,后来的读锁获取不了,会等待写锁完成后再获取读锁。

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)
    }()
    go func() {
        time.Sleep(time.Second)
        wg.Done()
    }()
    wg.Wait()
}

panic: sync: WaitGroup is reused before previous Wait has returned

双检查实现单例

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

可以编译,有并发问题,f函数可能会被执行多次

o.done在多CPU运行下会有并发问题,可使用atomic包解决问题

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

fatal error: all goroutines are asleep – deadlock! 死锁

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

可以编译,运行时内存可能暴涨

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: 2

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

panic: close of nil channel

map

package main

import (
    "fmt"
    "sync"
)

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

    fmt.Println(m.Len())
}

不能编译 没有len()方法

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

输出 1

自定义Map

package main

import "sync"

type Map struct {
    m map[int]int
    sync.Mutex
}

func (m *Map) Get(key int) (int, bool) {
    m.Lock()
    defer m.Unlock()

    i, ok := m.m[key]
    return i, ok
}

func (m *Map) Put(key, value int) {
    m.Lock()
    defer m.Unlock()
    m.m[key] = value
}

func (m *Map) Len() int {
    return len(m.m)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    m := Map{m: make(map[int]int)}
    go func() {
        for i := 0; i < 10000000; i++ {
            m.Put(i, i)
        }

        wg.Done()
    }()

    go func() {
        for i := 0; i < 10000000; i++ {
            m.Len()
        }

        wg.Done()
    }()

    wg.Wait()
}

可运行,有并发问题

slice

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    var ints = make([]int, 0, 1000)

    go func() {
        for i := 0; i < 1000; i++ {
            ints = append(ints, i)
        }
        wg.Done()
    }()

    go func() {
        for i := 0; i < 1000; i++ {
            ints = append(ints, i)
        }
        wg.Done()
    }()

    wg.Wait()
    fmt.Println(len(ints))
}

输出可能不是2000

文章永久链接:https://tech.souyunku.com/45594

未经允许不得转载:搜云库技术团队 » Go并发编程小测验答案解析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们