文章目录
- 1、语法基础
- 2、特性
- 3、Go常用命令
- 3.1、go build,编译Go源文件
- 3.2、go run,编译并运行Go程序
- 3.3、go get,下载并安装依赖或项目
- 3.4、go mod,模块支持
- 3.5、go list,列出包或模块
- 3.6、go fmt,自动格式化Go源代码
- 3.7、go vet,对Go代码进行静态分析
- 3.8、go test,运行Go程序的测试
- 3.9、go doc,查看Go语言标准库或你的代码库中的文档
- 3.10、go env,打印Go的环境信息
- 3.11、go clean,删除编译生成的文件
- 3.12、go tool,运行指定的Go工具
- 3.13、go version,打印当前Go的版本信息
- 3.14、go install,编译和安装Go程序或库
- 3.15、go generate,通过处理源代码来生成Go文件
- 3.16、go fix,更新包以使用新的API
- 3.17、go workspace,管理Go工作区
- 3.18、go help,查看命令或主题的帮助信息。
1、语法基础
1.1 环境安装
1.1.1 安装
Go语言中文网:https://studygolang.com/dl
Go语言英文网:https://go.dev/
标准库文档:https://pkg.go.dev/std
Linux安装
下载:https://go.dev/dl/go1.20.2.linux-amd64.tar.gz
# 解压
tar -zxvf go1.20.2.linux-amd64.tar.gz -C /usr/local
# 设置环境变量
export PATH=$PATH:/usr/local/go/bin
# 设置依赖包下载源国内源,国内可用的镜像源,可选设置其中一个
export GOPROXY=https://goproxy.cn # 七牛
export GOPROXY=https://goproxy.io
export GOPROXY=https://mirrors.aliyun.com/goproxy/ # 阿里云
Windows安装
下载:https://go.dev/dl/go1.20.2.windows-amd64.zip
同样解压和设置类似环境变量
1.1.2 编译工具
(1)go run命令
go run HelloGo.go
(1)go build命令
# -o指定生成的可执行文件名
go build -o HelloGo HelloGo.go
# 或者
go build HelloGo.go
1.2 基本语法
1.2.1 变量的声明与初始化
每一个声明的变量必须被使用,否则会编译不通过,变量声明样式:
# 格式
var 名称 类型
# 例如
var a int // 声明一个int类型的变量
var b string // 声明一个string类型的变量
var c []float // 声明一个float类型的切片
var d struct{ // 声明一个匿名结构体,结构体内有一个int字段
x int
}
var e func() bool // 声明一个函数变量
# 推导语法糖特性,精简的样式
var a = 100
# :=左值必须是未定义的变量,同时不能出现在全局变量的声明和初始化中
b := "Hello"
1.2.1 原生数据类型
1、整数
- 有符号:int8、int16、int32、int64
- 无符号:uint8、uint16、uint32、uint64
- 自匹配长度:int、uint
2、浮点数 - float32、float64
3、布尔型
true、false
字符串
字符串以原生数据类型出现,类同整型、布尔型,基于UTF-8编码实现,需要区分byte和rune。
f := "Go编程"
fmt.Printf("byte len is %v\n", len(f))
fmt.Printf("rune len is %v\n", utf8.RuneCountInString(f))
for _, g := range []byte(f) {
fmt.Printf("byte for:%c\n", g)
}
for _, h := range f {
fmt.Printf("rune for:%c\n", h)
}
1.2.2 指针
包含三个概念:指针类型、指针地址、指针取值
str := "Hello World"
strPrt := &str
fmt.Printf("类型: %T, 地址值: %v, 取值: %v", strPrt, strPrt, *strPrt)
#输出结果: 类型: *string, 地址值: 0xc000054250, 变量值: Hello World
1.2.3 常量与类型别名
常量定义
const str string = "Hello"
const str2 = "Hello"
类型别名
type myname1 = int // 定义一个类型别名
type mynate2 int // 定义一个新类型
func main(){
var a myname1 = 1
vra b mynate2 = 2
}
1.2.4 分支与循环
if 条件两边的“()”可以省略
if 条件 {
//执行的代码
} else if 条件 {
//执行的代码
}
switch 不需要break跳出,如果需要继续执行后边的case判断,需要加fallthrough,case支持数值常量、字符串、表达式的处理
如果在case中使用判断表达式,switch后边不需要指定判断变量
score := 90
switch {
case score < 100 && score >= 90:
fmt.Println("优秀")
default:
fmt.Println("分数错误")
}
for,go没有提供while、do while,可以使用break跳出循环,使用continus继续下一个循环,使用样式
for 初始值;条件;结束{
循环代码
}
1.3 容器
1.3.1 数组
语法格式:
# var 变量名 [数量]类型
var classMates1 [3]string
classMates1[0] = "Hello"
classMates1[1] = "World"
classMates2 := [...]string{"Hello", "World"}
切片
classMates3 := classMates2[0,1] // "Hello"
动态切片
// 格式 make([]类型, 长度, 容量)
a = make([]int, 2, 4) // 动态
1.3.2 列表
定义样式
import (
"container/list"
)
// 格式
//var name list.List // 或
//name := list.New()
func main() {
tmpList := list.New()
// 插入
for i:= 1 ; i <= 10 ; i++ {
tmpList.PushBack(i)
}
first := tmpList.PushFront(0)
// 删除
tmpList.Remove(first)
// 遍历
for l := tmpList.Front(); l != nil; l = l.Next(){
fmt.Print(l.Value, " ")
}
}
1.3.2 字典
定义格式
//name = make(map[类型]类型)
classMates1 := make(map[string]string)
classMates1["a"] = "Hello" // 添加映射关系
classMates2 := map[string]string{
"a": "Hello",
}
mate,ok := classMates2[1] // 如果存在,ok为true
1.3.3 容器遍历
package main
import (
"fmt"
)
func main() {
// 数组的遍历
nums := [...]int{1,2,3,4,5,6,7,8}
for k, v:= range nums{
// k为下标,v为对应的值
fmt.Println(k, v, " ")
}
// 切片的遍历
slis := []int{1,2,3,4,5,6,7,8}
for k, v:= range slis{
// k为下标,v为对应的值
fmt.Println(k, v, " ")
}
// 字典的遍历
tmpMap := map[int]string{
0 : "小明",
1 : "小红",
2 : "小张",
}
for k, v:= range tmpMap{
// k为键值,v为对应值
fmt.Println(k, v, " ")
}
}
1.4 函数与接口
1.4.1 函数声明和参数传递
函数格式
// 格式
func 函数名(参数名 类型,...)(返回参数 类型,...){
//函数体
}
// 例子
func cal(a, b int) int{ // 如果类型相比,可省略a类型
return a + b;
}
func div(a, b int)(returna, returnb int){
returna = a/b;
returnb = a%b;
return;
}
1.4.2 匿名函数和闭包
1.4.2.1 匿名函数
# 样式
funce(params)(return params){
// 函数体
}
# 例子
func (name string){
fmt.println("My name is ", name)
}
# 匿名函数赋值给变量
myFunc := func(){
fmt.Println(time.Now())
}
currentTime()
回调函数使用
func proc(input string, processor func(str string)){
processor(input)
}
func main(){
proc("hello", func(str string){
for _, v := range str{
fmt.Printf("%c\n",v)
}
})
}
1.4.2.2 闭包
// 闭包内封装了myvar内部变量,外部代码无法直接访问
func createCounter(myvar int) func() int {
if myvar < 0{
myvar = 0
}
// 引用myvar,创建一个闭包
return func() int{
myvar++
// 返回当前计数
return myvar;
}
}
func main() {
// 计数器1
c1 := createCounter(1)
fmt.Println(c1()) // 2
fmt.Println(c1()) // 3
// 计数器2
c2 := createCounter(10)
fmt.Println(c2()) // 11
fmt.Println(c1()) // 4
}
1.4.3 接口声明与嵌套
定义样式,定义需要使用type和interface关键词
type 接口名称 interface{
方法名(变量)(return 变量)
}
// 例子
type Tank interface{
Walk()
Fire()
}
1.4.4 函数体实现接口
// 接口定义
type Printer interface{
Print(interface}{)
}
// 函数定义为类型
type FuncCaller func(p interface{})
// 实现Printer的Print方法
func (funcCaller FuncCaller) Print(p interface{}) {
// 调用funcCaller函数本体
funcCaller(p)
}
func main() {
var printer Printer
// 将匿名函数强转为FuncCaller赋值给printer
printer = FuncCaller(func(p interface{}) {
fmt.Println(p)
})
printer.Print("Golang is Good!")
}
1.5 结构体和方法
1.5.1 结构体的定义
// 定义,
type 结构名 struct{
变量 类型
变量 类型
}
// 例子
Type Person struct{ // 结构体在包外被访问,结构名首字母大写
Name string //字段是公开的,字段名首字母必须大写
Birth string
ID int64
}
1.5.2 结构体的实例化和初始化
- 像声明基本类型一样实例化结构体
var p1 Person
p1.Name = "Hello"
p1.Birth = "2023-04-06"
- 使用new方式为结构体分配内存,这时返回的为结构体的指针,同样用.访问字段
p2 := new(Person)
p2.Name = "Hello"
p2.Birth = "2023-04-06"
- 实例化时也可以对字段进行初始化
p4 := Person{
Name : "Hello",
Birth : "2023-04-06"
}
- 全部字段初始化时,可以按字段顺序初始化值,并省略键值
p4 := Person{
"Hello",
"2023-04-06",
5
}
1.5.3 方法与接收器
方法定义样式
func (recipient recipientType) methodName(params)(return params){
// function body
}
例子
type Person struct {
Name string
Birth string
ID int64
}
func (person *Person) changeName(name string) {
person.Name = name
}
func (person Person) changeId(id int64) {
person.ID = id
}
func (person Person) printMess() {
fmt.Printf("My name is %v, and my birthday is %v, and my id is %v\n",
person.Name, person.Birth, person.ID)
}
func main() {
p1 := Person{
Name: "Hello",
Birth: "1991-10-23",
ID: 1,
}
p1.printMess()
p1.changeName("World")
p1.changeId(2)
p1.printMess()
}
1.5.4 结构体实现接口
用结构体实现接口
// Cat接口
type Cat interface {
// 抓老鼠
CatchMouse()
}
// Dog接口
type Dog interface {
// 吠叫
Bark()
}
type CatDog struct {
Name string
}
// 实现Cat接口
func (catDog *CatDog) CatchMouse() {
fmt.Printf("%v caught the mouse and ate it!\n", catDog.Name)
}
// Dog接口
func (catDog *CatDog) Bark() {
fmt.Printf("%v barked loudly!\n", catDog.Name)
}
func main() {
catDog := &CatDog{
"Lucy",
}
// 声明一个Cat接口,并将catDog指针类型赋值给cat
var cat Cat
cat = catDog
cat.CatchMouse()
// 声明一个Dog接口,并将catDog指针类型赋值给dog
var dog Dog
dog = catDog
dog.Bark()
}
1.5.5 内嵌和组合
定义
type 名称 struct{
string // 类型
int
}
结构体内嵌,可以直接访问内嵌结构体成员
type Wheel struct {
shape string
}
type Car struct {
Wheel
Name string
}
func main() {
car := &Car{
Wheel{
"圆形的",
},
"福特",
}
fmt.Println(car.Name, car.shape, " ")
}
可以通过结构体内嵌模拟面向对象的继承特性。
type Swimming struct {
}
func (swim *Swimming) swim() {
fmt.Println("swimming is my ability")
}
type Flying struct {
}
func (fly *Flying) fly() {
fmt.Println("flying is my ability")
}
type WildDuck struct {
Swimming
Flying
}
type DomesticDuck struct {
Swimming
}
func main() {
wild := WildDuck{}
wild.fly()
wild.swim()
domestic := DomesticDuck{}
domestic.swim()
}
2、特性
2.1 依赖管理
2.1.1 包管理
同一个包内定义的函数、类型、结构、变量、常量,都能被包内的其它代码直接访问,非同包内需要import导入。
如果函数、类型、结构、变量、常量位于不同包下,需要将它们的名称首字母大写,才能被其它包访问。
package定义包
package main
import导入包
import (
"fmt"
"os"
)
2.1.2 GOPATH
Go工作目录结构:
- src:源码,一个目录就是一个包;
- pkg:编译后的类库;
- bin:编译后的可执行程序;
GOPATH是Go语言的一个环境变量。 - go install:编译Go项目代码,并安装到GOPATH目录。
如果是包含main包和main函数,生成可执行文件到GOPATH/bin,可以直接运行;
如果没有,则生成.a应用包到GOPATH/pkg目录,可以被依赖调用; - go get:拉取依赖包。
go get通过git或svn下载远程代码到GOPATH/src目录,然后自动执行go install完成依赖编译和安装。
# 下载最新版本
go get github.com/gin-gonic/gin
# 下载指定版本
go get github.com/gin-gonic/gin@v1.9.0
# 常用命令
go mod init # 初始化生成go.mod
go mod tidy # 更新依赖文件
go mod download # 下载依赖文件
go mod vendor # 将依赖转移至本地的vendor文件
go mod edit # 手动修改依赖文件
go mod graph # 打印依赖图
go mod verify # 校验依赖
go mod why # 解释为什么需要依赖
2.1.3 Go Modules
1.12版本中正式支持Go Modules,包管理解决方案,只要包含go.mod文件即可。
创建新的Module:
go mod init 模块名
当前目录将会生成go.mod内容:
module test1
go 1.18
如果需要引入新的依赖test2,版本1.0.0,在go.mod中加入代码:
module test1
go 1.18
require github.com/test2 v1.0.0
下载依赖命令
# 手动下载依赖
go mod download
# 更新依赖关系,只下载缺少的模块,并移除不用的模块
go mod tidy
2.2 反射基础
通过反射,可以获取变量的名称、类型信息、结构信息。例子
import (
"fmt"
"reflect"
)
// 定义一个人的接口
type Person interface {
SayHello(name string)
Run() string
}
type Hero struct {
Name string
Age int
Speed int
}
func (hero *Hero) SayHello(name string) {
fmt.Println("Hello " + name, ", I am " + hero.Name)
}
func (hero *Hero) Run() string {
fmt.Println("I am running at speed ", hero.Speed)
return "Running"
}
2.2.1 reflect.Type
通过reflect.TypeOf()获取类型对象reflect.Type
typeOfHero := reflect.TypeOf(Hero{})
fmt.Printf("Hero's type is %s, kind is %s", typeOfHero, typeOfHero.Kind())
// 结构体类型为Struct;指针类型为Ptr,可以使用Type.Elem获取真实类型对象
fmt.Printf("*Hero's type is %s, kind is %s",reflect.TypeOf(&Hero{}), reflect.TypeOf(&Hero{}).Kind())
typeOfPtrHero := reflect.TypeOf(&Hero{})
fmt.Printf("*Hero's type is %s, kind is %s\n",typeOfPtrHero, typeOfPtrHero.Kind())
typeOfHero := typeOfPtrHero.Elem()
fmt.Printf(" typeOfPtrHero elem to typeOfHero, Hero's type is %s, kind is %s", typeOfHero, typeOfHero.Kind())
2.2.2 reflect.StruceField反射字段和reflect.Method反射方法
- Type接口提供的反射字段的方法
// 获取结构体内的字段数量
NumField() int
// 获取字段类型对象
Field(i int) StructField
// 根据字段名称获取字段类型对象
FieldByName(name string) (StructField, bool)
- 字段reflect.StructField的属性
type StructField struct{
// 字段的名称
Name string
// 字段的类型
Type Type
// Tag, 如果ID string 'json:"id"'
Tag StructTag
// 字节偏移
Offset uintptr
// 字段的index
Index []int
// 字段是否公开
Anonymous bool
}
使用例子
typeOfHero := reflect.TypeOf(Hero{})
// 通过 #NumField 获取结构体字段的数量
for i := 0 ; i < typeOfHero.NumField(); i++{
fmt.Printf("field' name is %s, type is %s, kind is %s\n",
typeOfHero.Field(i).Name,
typeOfHero.Field(i).Type,
typeOfHero.Field(i).Type.Kind())
}
// 获取名称为 Name 的成员字段类型对象
nameField, _ := typeOfHero.FieldByName("Name")
fmt.Printf("field' name is %s, type is %s, kind is %s\n", nameField.Name, nameField.Type, nameField.Type.Kind())
- Type接口提供的反射方法的方法
// 方法的数量
NumMethod() int
// 按索引号查找方法
Method(int) Method
// 按方法名查找方法
MethodByName(string) (Method, bool)
- 方法reflect.Method的属性
type Method struct{
// 方法名
Name string
// 类型
Type Tyoe
// 反射对象,可用于调用方法
Func Value
// 方法的索引号
Index int
}
使用例子
// 声明一个 Person 接口,并用 Hero 作为接收器
var person Person = &Hero{}
// 获取接口Person的类型对象
typeOfPerson := reflect.TypeOf(person)
// 打印Person的方法类型和名称
for i := 0 ; i < typeOfPerson.NumMethod(); i++{
fmt.Printf("method is %s, type is %s, kind is %s.\n",
typeOfPerson.Method(i).Name,
typeOfPerson.Method(i).Type,
typeOfPerson.Method(i).Type.Kind())
}
method, _ := typeOfPerson.MethodByName("Run")
fmt.Printf("method is %s, type is %s, kind is %s.\n", method.Name, method.Type, method.Type.Kind())
2.2.3 reflect.Value反射值
- 获取Value反射值
// 使用reflect.ValueOf获取反射值
reflect.ValueOf()
- 类型Value的方法
func (v Value) Interface() (i interface{})
// 返回int值
func (V Value) Int() int64
// 返回float值
func (v Value) Float() float64
// 返回[]byte值
func (v Value) Bytes() []byte
// 返回string值
func (v Value) String() string
// 返回bool值
func (v Value) Bool bool
使用例子
name := "小明"
valueOfName := reflect.ValueOf(name)
fmt.Println(valueOfName.Interface())
// 取值类型不匹配,提示panic错误
name := "小明"
valueOfName := reflect.ValueOf(name)
fmt.Println(valueOfName.Bytes())
typeOfHero := reflect.TypeOf(Hero{})
heroValue := reflect.New(typeOfHero)
fmt.Printf("Hero's type is %s, kind is %s\n", heroValue.Type(), heroValue.Kind())
// 修改Value值
name := "小明"
valueOfName := reflect.ValueOf(&name)
valueOfName.Elem().Set(reflect.ValueOf("小红"))
fmt.Println(name)
// 通过CanAddr判断是否可修改
name := "小明"
valueOfName := reflect.ValueOf(name)
fmt.Printf( "name can be address: %t\n", valueOfName.CanAddr())
valueOfName = reflect.ValueOf(&name)
fmt.Printf( "&name can be address: %t\n", valueOfName.CanAddr())
valueOfName = valueOfName.Elem()
fmt.Printf( "&name's Elem can be address: %t", valueOfName.CanAddr())
// 通过CanSet判断是否为公开变量,必须同步满足CanAddr和CanSet才能修改值
valueOfHero := reflect.ValueOf(hero).Elem()
valueOfName := valueOfHero.FieldByName("Name")
// 判断字段的 Value 是否可以设定变量值
if valueOfName.CanSet() {
valueOfName.Set(reflect.ValueOf("小张"))
}
fmt.Printf("hero name is %s", hero.Name)
调用接口方法例子
var person Person = &Hero{
Name: "小红",
Speed: 100,
}
valueOfPerson := reflect.ValueOf(person)
// 获取SayHello 方法
sayHelloMethod := valueOfPerson.MethodByName("SayHello")
// 构建调用参数并通过 #Call 调用方法
sayHelloMethod.Call([]reflect.Value{reflect.ValueOf("小张")})
// 获取Run 方法
runMethod := valueOfPerson.MethodByName("Run")
// 通过 #Call 调用方法并获取结果
result := runMethod.Call([]reflect.Value{})
fmt.Printf("result of run method is %s.", result[0])
var person Person = &Hero{
Name: "小红",
}
// 获取接口Person的类型对象
typeOfPerson := reflect.TypeOf(person)
// 打印Person的方法类型和名称
sayHelloMethod, _ := typeOfPerson.MethodByName("Run")
// 将 person 接收器放在参数的第一位
result:=sayHelloMethod.Func.Call([]reflect.Value{reflect.ValueOf(person)})
fmt.Printf("result of run method is %s.", result[0])
// 一般方法的反射调用
methodOfHello := reflect.ValueOf(hello)
methodOfHello.Call([]reflect.Value{})
func hello() {
fmt.Print("Hello World!")
}
2.3 并发
2.3.1 协程goroutine
使用goroutine语法格式
go 表达式
使用例子
import (
"fmt"
"time"
)
func test() {
fmt.Println("I am work in a single goroutine")
}
func main() {
go test()
time.Sleep(time.Second)
}
匿名函数
func main() {
go func(name string) {
fmt.Println("Hello " + name)
}("world")
time.Sleep(time.Second)
}
2.3.2 通道channel
通道作为goroutine之间同步和通信的工具。
# 发送数据到channel使用"<-"符号
chanel <- val
# 从channel读取数据
val, ok:= <- channel
# 创建channel
ch := make(chan T, size)
无缓存例子
func printInput(ch chan string) {
for val := range ch{
if val == "EOF"{
break
}
fmt.Printf("Input is %s\n", val)
}
}
func main() {
// 创建一个无缓冲的 channel
ch := make(chan string)
go printInput(ch)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
val := scanner.Text()
ch <- val
if val == "EOF"{
fmt.Println("End the game!")
break
}
}
defer close(ch)
}
带缓冲例子
func consume(ch chan int) {
// 线程休息 100s 再从 channel 读取数据
time.Sleep(time.Second * 100)
<- ch
}
func main() {
// 创建一个长度为 2 的 channel
ch := make(chan int, 2)
go consume(ch)
ch <- 0
ch <- 1
// 发送数据不被阻塞
fmt.Println("I am free!")
ch <- 2
fmt.Println("I can not go there within 100s!")
time.Sleep(time.Second)
}
使用select读取
func send(ch chan int, begin int ) {
for i :=begin ; i< begin + 10 ;i++{
ch <- i
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go send(ch1, 0)
go send(ch2, 10)
// 主 goroutine 休眠 1s,保证调度成功
time.Sleep(time.Second)
for {
select {
case val := <- ch1: // 从 ch1 读取数据
fmt.Printf("get value %d from ch1\n", val)
case val := <- ch2 : // 从 ch2 读取数据
fmt.Printf("get value %d from ch2\n", val)
case <-time.After(2 * time.Second): // 超时设置
fmt.Println("Time out")
return
}
}
}
2.3.3 sync同步锁
2.3.3.1 Mutex(互斥锁)
sync.Mutex方法
Lock()
Unlock()
例子
import (
"fmt"
"sync"
"time"
)
func main() {
var lock sync.Mutex
go func() {
// 加锁
lock.Lock()
defer lock.Unlock()
fmt.Println("func1 get lock at " + time.Now().String())
time.Sleep(time.Second)
fmt.Println("func1 release lock " + time.Now().String())
}()
time.Sleep(time.Second / 10)
go func() {
lock.Lock()
defer lock.Unlock()
fmt.Println("func2 get lock " + time.Now().String())
time.Sleep(time.Second)
fmt.Println("func2 release lock " + time.Now().String())
}()
// 等待 所有 goroutine 执行完毕
time.Sleep(time.Second * 4)
}
2.3.3.2 RWMutex(读写锁)
sync.RWMutex提供的方法
// 写加锁
func (rw *RWMutex) Lock()
// 写解锁
func (rw *RWMutex) Unlock()
// 读加锁
func (rw *RWMutex) RLock()
// 读解锁
func (rw *RWMutex) RUnlock()
例子
import (
"fmt"
"strconv"
"sync"
"time"
)
func main() {
var rwLock sync.RWMutex
// 获取读锁
for i := 0 ; i < 5 ;i ++{
go func(i int) {
rwLock.RLock()
defer rwLock.RUnlock()
fmt.Println("read func " + strconv.Itoa(i) +" get rlock at " + time.Now().String())
time.Sleep(time.Second)
}(i)
}
time.Sleep(time.Second / 10)
// 获取写锁
for i := 0 ; i < 5; i++{
go func(i int) {
rwLock.Lock()
defer rwLock.Unlock()
fmt.Println("write func " + strconv.Itoa(i) +" get wlock at " + time.Now().String())
time.Sleep(time.Second)
}(i)
}
time.Sleep(time.Second * 10)
}
2.3.3.3 WaitGroup(读写锁)
sync.WaitGroup接口方法
// 添加等待数量,
func(wg *WaitGroup) Add(delta int)
// 等待数量减1
func(wg *WaitGroup) Done()
// 等待
func(wg *WaitGroup) Wait()
例子
import (
"fmt"
"strconv"
"sync"
"time"
)
func main() {
var waitGroup sync.WaitGroup
waitGroup.Add(5)
for i := 0 ; i < 5 ; i++{
go func(i int) {
fmt.Println("work " + strconv.Itoa(i) + " is done at " + time.Now().String())
// 等待 1 s 后减少等待数
time.Sleep(time.Second)
waitGroup.Done()
}(i)
}
waitGroup.Wait()
fmt.Println("all works are done at " + time.Now().String())
}
2.3.3.4 Mapp(并发安全字典)
sync.Map接口方法
// 删除操作
func (m *Map) Delete(key interface{})
// 读操作
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
// 读取或写入。存在指定的 key 则读取,否则写入
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
// 写操作
func (m *Map) Store(key, value interface{})
// 遍历
func (m *Map) Range(f func(key, value interface{}) bool)
使用例子
import (
"fmt"
"strconv"
"sync"
)
func addNumber(begin int) {
for i := begin ; i < begin + 3 ; i++{
syncMap.Store(i, i)
}
waitGroup.Done()
}
var syncMap sync.Map
var waitGroup sync.WaitGroup
func main() {
routineSize := 5
waitGroup.Add(routineSize)
for i := 0; i < routineSize; i++ {
go addNumber(i * 10)
}
waitGroup.Wait()
var size int
syncMap.Range(func(key, value interface{}) bool {
size++
//fmt.Println("key-value pair is", key, value, " ")
return true
})
fmt.Println("syncMap current size is " + strconv.Itoa(size))
value, ok := syncMap.Load(0)
if ok {
fmt.Println("key 0 has value", value, " ")
}
}
3、Go常用命令
3.1、go build,编译Go源文件
go build命令用于编译Go源文件。该命令会根据源代码生成可执行文件或库。
$ go build main.go
# 无输出,但会生成一个名为main的可执行文件
3.2、go run,编译并运行Go程序
go run命令用于编译并运行Go程序。适用于快速测试代码片段。
$ go run main.go
main, world!
3.3、go get,下载并安装依赖或项目
go get用于下载并安装依赖或项目。
$ go get github.com/gin-gonic/gin
# 下载并安装gin库,无输出
3.4、go mod,模块支持
go mod用于Go模块支持,包括初始化、添加依赖等。
$ go mod init my-module
go: creating new go.mod: module my-module
3.5、go list,列出包或模块
go list用于列出包或模块。
$ go list ./...
# 列出当前项目所有包
3.6、go fmt,自动格式化Go源代码
go fmt用于自动格式化Go源代码。
$ go fmt main.go
# 格式化main.go文件,返回格式化后的文件名
main.go
3.7、go vet,对Go代码进行静态分析
go vet用于对Go代码进行静态分析,检查可能存在的错误。
$ go vet main.go
# 若代码无问题,则没有输出
3.8、go test,运行Go程序的测试
go test用于运行Go程序的测试。
$ go test
ok github.com/yourusername/yourpackage 0.002s
3.9、go doc,查看Go语言标准库或你的代码库中的文档
go doc用于查看Go语言标准库或你的代码库中的文档。
$ go doc fmt.Println
func Println(a ...interface{}) (n int, err error)
3.10、go env,打印Go的环境信息
go env用于打印Go的环境信息。
$ go env
GOARCH="amd64"
GOBIN=""
...
3.11、go clean,删除编译生成的文件
go clean用于删除编译生成的文件。
$ go clean
# 删除编译生成的文件,无输出
3.12、go tool,运行指定的Go工具
go tool用于运行指定的Go工具。
$ go tool compile main.go
# 编译main.go,生成中间文件
3.13、go version,打印当前Go的版本信息
go version用于打印当前Go的版本信息。
$ go version
go version go1.17.1 linux/amd64
3.14、go install,编译和安装Go程序或库
go install用于编译和安装Go程序或库。
$ go install main.go
# 编译并安装main程序,无输出
3.15、go generate,通过处理源代码来生成Go文件
go generate用于通过处理源代码来生成Go文件。
$ go generate
# 运行生成指令,生成代码,无输出
3.16、go fix,更新包以使用新的API
go fix用于更新包以使用新的API。
$ go fix oldpackage
# 更新oldpackage包的API调用,无输出
3.17、go workspace,管理Go工作区
go workspace用于管理Go工作区。这是一个实验性功能。
$ go workspace create myworkspace
# 创建名为myworkspace的工作区,无输出
3.18、go help,查看命令或主题的帮助信息。
go help用于查看命令或主题的帮助信息。
$ go help build
# 显示go build命令的详细帮助信息