Go并发编程 | 青训营笔记

36 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

1、并发与并行的区别

并发:多线程程序在一个核的CPU上运行

image.png

并行:多线程程序在多个核的CPU上运行

image.png

Go可以充分发挥多核优势 高效运行

2、协程Goroutine

协程:用户态,轻量级线程 栈MB级别

线程:内核态,线程跑多个协程,栈KB级别

image.png

快速打印hello goroutine

func hello(i int) {
    println("hello goroutine : " + fmt.Sprint(i))
}
​
func HelloGoRoutine() {
    for i := 0; i < 5; i++ {
        go func(j int) {
            hello(j)
        }(i)
    }
    time.Sleep(time.Second)
}

3、CSP并发模型

提倡通过通信共享内存而不是通过共享内存而实现通信,因为通过共享内存实现通信容易发生数据竞态的问题,影响程序的性能。

image.png

4、Channel 缓冲通道

make(chan 元素类型, [缓冲大小])

无缓冲通道(同步通道) make(chan int)

有缓冲通道 make(chan int, 2)

image.png

计算输入数字的平方

func CalSquare() {
    src := make(chan int)
    dest := make(chan int, 3)
    go func() {
        defer close(src)
        for i := 0; i < 10; i++ {
            src <- i
        }
    }()
    go func() {
        defer close(dest)
        for i := range src {
            dest <- i * i
        }
    }()
    for i := range dest {
        println(i)
    }
}

5、并发安全

避免对共享内存做一些非并发安全的读写操作

互斥锁解决数据竞态

var (
    x    int64
    lock sync.Mutex
)
​
func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
    }
}
func addWithoutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}
​
func Add() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    time.Sleep(time.Second)
    println("WithoutLock:", x)
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    time.Sleep(time.Second)
    println("WithLock:", x)
}

6、WaitGroup

计数器

开始协程+1;执行结束-1;主进程阻塞直到计数器为0

快速打印hello WaitGroup

func hello(i int) {
    println("hello WaitGroup :" + fmt.Sprint(i))
}
​
func ManyGoWait() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(j int) {
            defer wg.Done()
            hello(j)
        }(i)
    }
    wg.Wait()
}

7、小结

1、Go可以通过高效的调度模型实现协程的高并发操作。

2、Go提倡通过通信来实现共享内存。

3、sycn包下包含lock和WaitGroup关键字来实现并发安全操作和协程间的同步。