Go 语言入门指南:基础语法和常用特性解析
项目创建
GOPATH 模式
GO111MODULE=off
GOROOT:Go语言安装目录,Go的编译器、标准库等都存放在这个目录下
GOPATH:Go语言工作空间
- bin:存放编译后的可以执行文件
- pkg:存放编译后的库文件(在Go Mod模式下也存放第三方库源码)
- src:存放源码,在项目目录中进行代码开发
GOPATH 模式下创建的项目统一在GOPATH/src目录内
GOPATH 模式的所有的依赖包都是存放在 GOPATH下,没有包多版本控制功能
Go Mod 模式
GO111MODULE=on
Go Mod 模式可以在任意位置创建项目,但还是会把下载的依赖储存在 GOPATH/pkg/mod 中,也会把 go install 命令的结果放在 GOPATH/bin 中
Go Mod 模式具有包多版本控制功能,项目根目录中具有go.mod文件进行依赖管理
基本语法
- 声明变量并赋值
var a int = 1
const a int = 1
a := 1
- 数组
// 声明
var array [3]int
var twoDim [2][3]int
// 声明并赋值
var array = [3]int{1, 2, 3}
array := [5]int{1, 2, 3, 4, 5}
// range
for i, num := range array {
fmt.Println(array[i], num)
}
- 切片,即长度可变的动态数组
// 通过未指定大小的数组来定义切片
var slice []int
// 通过 make 定义切片
slice := make([]int, len)
- map
m := map[string]int{"one": 1, "two": 2}
m := make(map[string]int)
// range
for k, v := range m {
fmt.Println(k, v)
}
- struct
// 结构体
type user struct {
Name string
Password string
}
// 结构体方法
func (u user) checkPassword(password string) bool {
return u.Password == password
}
func (u *user) resetPassword(password string) {
u.Password = password
}
// 使用方法
a := user{Name: "wang", Password: "1024"}
a.resetPassword("2048")
a.checkPassword("2048")
// struct 和 json 的转化
buf, err := json.Marshal(a)
fmt.Println(string(buf))
var b userInfo
err = json.Unmarshal(buf, &b)
- 函数
func findUser(users []user, name string) (*user, error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
_, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {// nil 代表为空
// 错误处理
panic(err)
}
- 循环
for i := 0;i < 5; i++ {
......
}
- 分支
// Go 中的 switch 不需要使用 break
switch {
case a >= 0:
...
default:
...
}
- 命令行和环境变量
fmt.Println(os.Args) // 命令行参数
fmt.Println(os.Getenv("PATH")) // PATH 环境变量
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
并发编程
协程
严格意义上来说线程分为内核级线程和用户级线程,通常所说的线程指的是内核级线程,而用户级线程则就是协程,协程是轻量级线程
- 线程(内核级):操作系统有感知,由操作系统进行调度
- 协程(用户级):操作系统无感知,由用户进行调度,Golang中具有原生协程,由Golang进行管理
通道
通道用于协程之间通过传递一个指定类型的值来同步运行和通讯,操作符 <- 用于指定通道的方向
-
无缓冲通道,发送方会阻塞直到接收方从通道中接收了值
Println 顺序为:"接收方正在接收"、"接收方已接收"(同步通信)
// 无缓冲通道 func increase(N int, c chan int) { c <- N + 1 // 发送到通道 c fmt.Println("接收方已接收") } func main() { c := make(chan int) go increase(1, c) fmt.Println("接收方正在接收") x := <-c // 从通道 c 中接收 fmt.Println(x) } -
有缓冲通道,发送方不会阻塞(除非缓冲区已满),数据可以放在缓冲区里面,不需要接收端立刻去获取数据
Println 顺序为:"接收方已接收"、"接收方正在接收"(异步通信)
// 有缓冲通道 func increase(N int, c chan int) { c <- N + 1 // 发送到通道 c fmt.Println("接收方已接收") } func main() { c := make(chan int, 1)// 指定缓冲大小为 go increase(1, c) time.Sleep(1000) fmt.Println("接收方正在接收") x := <-c // 从通道 c 中接收 fmt.Println(x) }
使用 range 遍历通道
func Numbers(c chan int) {
defer close(c)
for i := 0; i < 10; i++ {
c <- i
}
}
func main() {
c := make(chan int, 10)
go Numbers(c)
// range 函数遍历每个从通道接收到的数据
// 发送完 10 个数据后 close 了通道,所以 range 函数在接收到 10 个数据之后便结束
// 如果不 close,那么 range 函数就不会结束,阻塞等待接收第 11 个数据
for i := range c {
fmt.Println(i)
}
}
并发控制
-
锁
sync.Mutexvar ( sharedValuable int lock sync.Mutex ) -
同步
sync.WaitGroupfunc test(wg *sync.WaitGroup) { wg.Done() // Done 一下 } func main() { var wg sync.WaitGroup wg.Add(5) // 需要等待 Done 五下 for i := 0; i < 5; i++ { go test(&wg) } wg.Wait() // 等待 }