Go 语言进阶与依赖管理(二)|青训营笔记

88 阅读1分钟

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

Go 语言进阶与依赖管理| 青训营笔记 - 掘金 (juejin.cn)

并发安全 Lock

Go语言中通过Groutine 启动一个Go协程,不同协程之间是并发执行的,就像C++/Java中线程之间线程安全是一个常见的问题。

package main

import (
	"fmt"
	"time"
)

func hello(i int) {
	println("hello:" + fmt.Sprint((i)))
}

func main() {
	cnt := 0
	for i := 0; i < 5000; i++ {
		go func() {
			cnt++
			hello(cnt)
		}()
	}
	time.Sleep(time.Second * 1)
	print(cnt)
}

希望的cnt的结果为5000,实际结果与期望不符

Go中提供了Mutex和RWMutex ,来实现锁功能。前者就是正常的Lock, Unlock;后者提供了读写锁,在协程读写分离中提供了读读互不影响,读写互斥。

package main

import (
	"fmt"
	"sync"
	"time"
)

func hello(i int) {
	println("hello:" + fmt.Sprint((i)))
}

func main() {
	cnt := 0
	var mu sync.RWMutex // rwmutex lock
	for i := 0; i < 5000; i++ {
		go func() {
			defer func() {
				mu.Unlock()
			}()
			mu.Lock()
			cnt++
			hello(cnt)
		}()
	}
	time.Sleep(time.Second * 1)
	print(cnt)
}

WaitGroup

Go语言提供了WaitGroup机制,就像是C++中的条件变量,主线程可以等待在条件变量上,当线程执行完可以通过条件变量唤醒主线程继续执行。

package main

import (
	"fmt"
	"sync"
)

func hello(i int) {
	println("hello:" + fmt.Sprint((i)))
}

func main() {
	cnt := 0
	var mu sync.RWMutex // rwmutex lock
	var wg sync.WaitGroup
	for i := 0; i < 5000; i++ {
		wg.Add(1)
		go func() {
			defer func() {
				mu.Unlock()
			}()
			mu.Lock()
			cnt++
			hello(cnt)
			wg.Done()
		}()
	}
	wg.Wait()
	print(cnt)
}

依赖管理

代码的高复用性是优秀代码必备的品质,而 Go 的包管理就为我们封装模块和复用代码提供了强有力的帮助。

GOPATH

在安装过程中我们曾设置过环境变量GOPATH,目录有以下结构:src:存放Go项目的源码;pkg:存放编译的中间产物,加快编译速度;bin:存放Go项目编译生成的二进制文件。

此时项目A和项目B依赖于某个package的不同版本,src下只能有一个版本的存在,则两个项目无法保证能成功编译。

govender

Vendor 是当前项目中的一个目录,其中存放了当前项目依赖的副本。在Vendor机制下,如果当前项目存在Vendor目录,会优先使用该目录下的依赖,如果依赖不存在,会从GOPATH中寻找;但vendor无法很好解决依赖包的版本变动问题和一个项目依赖同一个包的不同版本的问题

Go Modules

如果成功编译了go的代码就会发现,需要先在目录下生成一个名为go.mod的文件。我们可以通过go.mod文件管理依赖包版本,通过go get/go mod指令工具管理包

打开cmd,在go env中可以看到GO111MODULE。GO111MODULE 有三个值:off, on和auto(默认值)。

GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。

GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。

GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。

初始化

go mod init hello

此时查看go.mod文件

module hello

go 1.19

go.mod 提供了module, require、replace和exclude 四个命令

module 语句指定包的名字(路径) require 语句指定的依赖项模块 replace 语句可以替换依赖项模块 exclude 语句可以忽略依赖项模块

添加依赖

运行如下代码

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

会发现 go mod 会自动查找依赖自动下载

升级

运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号) 运行 go get -u=patch 将会升级到最新的修订版本 运行 go get package@version 将会升级到指定的版本号version 运行go get如果有版本的更改,那么go.mod文件也会更改

使用replace替换无法直接获取的package

modules 可以通过在 go.mod 文件中使用 replace 指令替换成github上对应的库,比如:

replace ( golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a )