Go并发进阶 | 青训营笔记

45 阅读3分钟

Go并发笔记

并行是实现并发的一个手段

并发:宏观上并行,微观交替执行。简单举例是多线程程序在一个核的CPU上运行

并发:真正实现同时运行,需要空间支持,比如多线程程序在多个核的cpu上同时运行

Go可以充分发挥多核优势

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

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

  • 举例:利用协程可以比for循环更快速的打印循环的效果
func hello(i int) {
	println("hello goroutine :" + fmt.Sprint(i))
}

func HelloGoRoutine() {
	for i := 0; i < 5; i ++ {
		go func(j int) {  //函数前加关键字go就会使得函数以协程来运行
			hello(j)
		}(i)
	}
	time.Sleep(time.Second)  //阻塞,保证协程执行完之前主线程不退出
}

协程之间的通信:CSP

提倡通过通信去共享内存,而不是通过共享内存实现通信

Channel

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

  • 无缓冲通道 make(chan int) 同步通道
  • 有缓冲通道 make(chan int, 2)

举例:A协程产生数据,B协程计算数据的平方,可以保证顺序性

func CalSquare() {  //类似于生产者消费者模型
    src := make(chan int)
    dest := make(chan int, 3)
    go func() {  //A协程
        defer close(src)
        for i := 0; i < 10; i ++ {
            src <- i
        }
    }()
    go func() {  //B协程
        defer close(dest)
        for i := range src {
            dest <- i * i
        }
    }()
    for i := range dest {  //主协程
        println(i)
    }
}

defer:

  • defer一般用于资源的释放和异常的捕捉, 作为Go语言的特性之一.

  • defer 语句会将其后面跟随的语句进行延迟处理. 意思就是说 跟在defer后面的语言 将会在程序进行最后的return之后再执行.

  • 在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。

并发安全Lock

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(x)  //8382
    x = 0
    for i:= 0; i < 5; i ++ {
        go addWithLock()
    }
    println(x)  //10000
}

这里其实就类似于多线程编程了,这里运用了互斥锁mutex,lock代表上锁,上锁之后其他线程无法访问临界区,unlock之后可以访问临界区,防止多个线程(这里是协程)同时访问同一个内存单元导致结果不正确。

WaitGroup

Add(delta int) 计数器+delta,一共delta个任务

Done() 每执行完一个任务,计数器-1

Wait() 阻塞直到计数器为0

func hello(i int) {
	println("hello goroutine :" + 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()
}

这段代码先创造了5个任务,调用wait表示这5个任务若还没有执行完,则不会退出

go启动了协程,一共启动5个协程

defer表示只有在该协程执行完hello之后才会关闭该协程

wait则保证了所有协程结束前主函数不会停止。