Go进阶|青训营笔记

58 阅读1分钟

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

语言进阶

1. Goroutine 协程

线程是属于内核态,创建、切换、停止都属于比较重的系统操作,栈KB级别。
协程可以理解成用户级、轻量级的线程,调度由Go语言本身去完成,栈MB级别。

func hello(i int){
    println("hello goroutine:"+fmt.Sprint(i))
}
// 快速打印hello goroutine:0~4
func HelloGoRoutine(){
    for i := 0; i<5; i++{
        // 调用函数时在函数前加上 go 关键字 便为函数创建一个协程来运行
        go func(j int){
            hello(j)
        }(i) // 匿名函数传入参数
    }
    // 保证子协程在完成之前主协程不退出
    time.Sleep(time.Second)
}
image.png

2. CSP(Communicating Sequential Processes)

image (1).png

Go提倡使用通信共享内存(左),而不是使用共享内存来实现通信(右)。使用共享内存来通信的话不可避免的要对临界区加锁,从而影响性能。

3. 通道 Channel

image (2).png

Channel的创建:

make(chan 元素类型, [缓冲大小])
make(chan int) // 无缓冲通道
make(chan int,2) // 有缓冲通道

使用无缓冲通道通信时,会导致发送方goroutine和接收方goroutine同步化,也被称为同步通道。解决同步问题的方式即使用有缓冲通道。

func CalSquare() {
	src := make(chan int)
	dest := make(chan int, 3)
        // 子协程发送0~9数字
	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)
	}
}

4. 并发安全 Lock

对变量执行2000次+1操作,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) // WithoutLock: 8382 不加锁输出未知结果
    x = 0
    for i:=0; i<5; i++{
        go addWithLock()
    }
    time.Sleep(time.Second)
    println("WithLock:", x) // WithLock: 10000
}

5. WaitGroup

image (3).png

开启协程+1,执行结束-1,主协程阻塞直到计数器为0。

image (4).png