Go进阶与依赖管理|青训营笔记

59 阅读1分钟

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

由于我观看这节课视频的时候总是显示音频出现问题,直接卡住,所以内容较少

主要内容

  • 锁 Lock
  • 线程同步 WaitGroup
  • GO Module

详细介绍

Goroutine

线程:内核态,栈MB级别; 协程:用户态,栈是KB级别的,可以理解为轻量级线程;

一个线程可以启动多个协程,协程的创建和调度由Go语言本身去完成。我们可以一次启动上万的协程,这就是Go更适合高并发原因,当我们需要快速处理某些事情时,就可以启动多个协程并发完成。

在Go语言中,用go关键字开启一个Goroutine

package main

import (
	"fmt"
	"time"
)

func showMsg(msg string) {
	for i := 0; i < 5; i++ {
		fmt.Printf("msg: %v\n", msg)
		time.Sleep(time.Millisecond*100)
	}
}

func main() {
	go showMsg("java")
	showMsg("golang")
}

在这个程序中,输出的顺序是不定的,而且 java输出的个数可能少于5个。顺序不一定的原因是主协程和其他线程是并发执行的,并没有先后顺序之分;java输出的个数可能少于5个的原因是当主协程执行完成,程序会自动退出,这时其他协程不一定执行完成,为了让所有的java都打印输出,可以在main函数的最后添加一个time.Sleep(time.Second)等待一会,后面将介绍WaitGroup来优雅的实现协程同步。

CSP

Go是通过通信来共享内存,而不是共享内存来实现通信

channel

channel是先进先出的,分为有缓存和无缓存两种

package main

import (
	"fmt"
	"math/rand"
	"time"
)

var values = make(chan int)

func send() {
	rand.Seed(time.Now().UnixNano())
	value:=rand.Intn(10)
	fmt.Printf("send: %v\n", value)
	time.Sleep(time.Second)
	values<-value
}

func main() {
	defer close(values)
	go send()
	fmt.Println("wait...")
	value:=<-values
	fmt.Printf("receive: %v\n", value)
	fmt.Println("end...")
}

Lock-mutex

package main

import (
	"fmt"
	"sync"
)

var i int = 100

var lock sync.Mutex
var wg sync.WaitGroup

func add() {
	defer wg.Done()
	lock.Lock()//加锁后,在解锁前其他资源无法访问i
	i += 1
	fmt.Printf("i++: %v\n", i)
	lock.Unlock()
}

func sub(){
	defer wg.Done()
	lock.Lock()
	i-=1
	fmt.Printf("i--: %v\n", i)
	lock.Unlock()
}

func main() {
	for t := 0; t < 100; t++ {
		wg.Add(2)
		go sub()
		go add()
	}
	wg.Wait()
	fmt.Printf("end>>>>i: %v\n", i)
}

WaitGroup

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func showMsg(i int) {
	defer wg.Done()//相当于wg.Add(-1)
	fmt.Printf("i: %v\n", i)
}

func main() {
	for i:=0;i<10;i++ {
		go showMsg(i)
		wg.Add(1)
	}

	wg.Wait()//这里只有在为0的时候才会往下执行,否则会一直等待
	fmt.Println("end...")
}

Go Module

以前Go诞生过许多的包管理方式,都比较麻烦,而且对程序文件放置的位置也有限制,而现在使用go mod管理包,只需要在源文件的根目录下执行go mod init package_name即可