Go语言进阶,Day2 | 青训营

81 阅读3分钟

并发编程

协程Goroutine:Goroutine由Go运行时(runtime)管理,而不是操作系统内核。它比线程更轻量。启动Goroutine的代价很低,只需要在调用函数前加上go关键字。Goroutine让并发编程变得非常简单。我们只需在逻辑上将程序分解为多个并发任务,然后用go语句启动每个并发任务即可。运行时会做好线程管理、时间片调度等工作。

Goroutine使用协作式调度,而非抢占式,需要主动让出控制权。

Channel:Goroutine通过Channel进行通信

锁和线程同步:Go语言实现线程同步的主要方式是通过内置的sync包中的锁机制。

常用的锁类型包括: Mutex:基本的互斥锁,用于保护共享资源的同步访问。

RWMutex:读写互斥锁,允许多个只读线程同时访问,但只允许一个写线程访问。

WaitGroup:用于等待一组goroutine都完成的同步工具。

Cond:条件变量,用于复杂的线程同步场景。

Once:只执行一次,用于单例模式的实现。 使用锁的主要步骤:

  1. 使用var初始化一个锁。
  2. 在可能产生冲突的goroutine中使用lock()上锁。
  3. 在冲突访问完成后使用unlock()解锁。需要注意避免死锁的发生,同时锁的粒度要合适,过于细致会影响效率。

除锁外,Go还通过channel提供了基于通信的线程同步方式,是Go语言鼓励的先进的同步范式。

func main() {
	fmt.Println("hello world")
	add()
	//CalSquare()
}

var (
	x    int64
	lock sync.Mutex
)

func addWithLock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()
		x = x + 1
		lock.Unlock()
	}

}

func addWithoutLock() {
	for i := 0; i < 2000; i++ {
		x = x + 1
	}
}

func add() {
	x = 0
	for i := 0; i < 5; i++ {
		go addWithoutLock()
	}
	time.Sleep(time.Second)
	fmt.Println("WithoutLock", x)
	x = 0
	for i := 0; i < 5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	fmt.Println("WithLock", x)
}

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 x := range src {
			dest <- x * x
		}
	}()
	for x := range dest {
		fmt.Println(x)
	}

}

Go Module:Go Module是Go语言从1.11版本开始正式推出的依赖管理系统。

它具有以下主要特征:

  1. 使用go.mod文件定义依赖:每个模块都有一个go.mod文件,明确声明依赖了哪些模块及版本。
  2. 支持语义化版本规范:依赖版本可以使用语义化版本号,如v1.2.3。
  3. 去中心化:不依赖中心仓库,可以使用任意仓库。
  4. 自动化vendor:通过go mod vendor可以自动把所有依赖打包进vendor目录。
  5. 向后兼容:老项目可以平滑过渡到模块项目。
  6. 支持GOPROXY:可配置代理来避免外网访问和提升下载速度。
  7. 支持Go Module Mirror:可以配置镜像加速模块访问。

Go Module为Go语言带来了语义化版本、离线构建、可复现的构建等特性,是包管理的重要进步。它是Go语言打造可靠、可扩展、安全软件的重要基石。