Go 语言入门指南:基础语法和常用特性解析 | 青训营

95 阅读4分钟

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{123}
array := [5]int{12345}
// range
for i, num := range array {
fmt.Println(array[i], num)
}
  • 切片,即长度可变的动态数组
// 通过未指定大小的数组来定义切片
var slice []int
// 通过 make 定义切片
slice := make([]intlen)
  • 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 stringbool {
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 < 5i++ {
......
}
  • 分支
// 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 int1)// 指定缓冲大小为
    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 int10)
go Numbers(c)
// range 函数遍历每个从通道接收到的数据
// 发送完 10 个数据后 close 了通道,所以 range 函数在接收到 10 个数据之后便结束
// 如果不 close,那么 range 函数就不会结束,阻塞等待接收第 11 个数据
for i := range c {
fmt.Println(i)
}
}

并发控制

  • sync.Mutex

    var (
    sharedValuable    int
    lock sync.Mutex
    )
    
  • 同步 sync.WaitGroup

    func test(wg *sync.WaitGroup) {
    wg.Done() // Done 一下
    }
    ​
    func main() {
    var wg sync.WaitGroup
    wg.Add(5// 需要等待 Done 五下
    for i := 0i < 5i++ {
    go test(&wg)
    }
    wg.Wait() // 等待
    }