Go中神奇的内置数据结构

本节内容是自己学习 Go高级工程师实战营 课程中 神奇的内置数据结构一节所做的笔记

概览

概览图:y3h5kk

  1. semaphore是信号量,是所有锁实现的基础,iface与eface是interface底层实现相关的
  2. netpoll就是我们之前讲到的runtime的四座大山,里面也有一些自定义数据结构,会在之后讲网络编程或者web框架的时候说
  3. sync是同步相关的,sync.Mutex是互斥锁相关的,sync.Pool是对象池,sync.Once必须只在只能初始化一次的函数中使用,waitgroup是汇集多个并发执行的结果
  4. os比较简单,因为最复杂的在底层进行了封装,我们是看不到的
  5. 内存分配会专门在gc中讲解
  6. context是Go 1.6增加的数据结构,也是值得去讲的

channel

两种创建方式

  1. 不带缓冲的:其实是有buffer的特殊情况,可以理解为buffer size为0的buffered chan。ch := make(chan int)
  2. 带缓冲的:会生成一个hchan数据结构,dataqsize(队列的大小)将会是我们定义的缓冲区的大小,ch := make(chan int, 3)

channel结构

如下的hchan结构体就是我们channel的结构,点击查看channel源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters

// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}

一张图片生动形象的介绍channel的数据结构:9lOZyp

go

gin+docker部署Golang应用

使用gin编写Go的后端程序,然后使用docker打包成镜像并在我们的阿里云ECS上进行部署

编写Go后端程序

项目目录如下:

1
2
3
4
go-web
├── go.mod
├── main.go
├── Dockerfile

main.go文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
"github.com/gin-gonic/gin"
"net/http"
"time"
)

/**
* @Author: yirufeng
* @Date: 2021/7/10 9:04 下午
* @Desc:
**/

type User struct {
Username string `json:"username"`
}

func main() {
engine := gin.Default()

engine.GET("/", func(c *gin.Context) {
startTime := time.Now()

c.JSON(http.StatusOK, gin.H{
"method": http.MethodGet,
"elapsedTime/ms": time.Since(startTime).Milliseconds(),
})
})

engine.POST("/", func(c *gin.Context) {
startTime := time.Now()

var u User
err := c.BindJSON(&u)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"error": err.Error(),
})
return
}

c.JSON(http.StatusOK, gin.H{
"method": http.MethodPost,
"elapsedTime/ms": time.Since(startTime).Milliseconds(),
"username": u.Username,
})
})

engine.Run(":8081")
}
go

gorm学习使用

gorm的使用安装 使用指令进行安装:go get github.com/jinzhu/gorm DDL操作前提 新建一个数据库gorm_project 指定使用Utf8编码 数据库连接 对应的代码 12345678910111213141516171819202122232425262728293031323334353637...
go

基本数据结构-map

什么是map 为什么要用map map的底层如何实现 map内存模型 创建map 哈希函数 key定位过程 map的两种get操作 【面试重点】如何进行扩容 【重点】map的遍历 map的赋值 map的删除 map进阶 可以边遍历边删除么 key可以是float类型么 总结 参考资料 面试经常被问到golang map的扩容,特此记录下来 map的结构 golang中map是...

基本数据结构-slice

查看slice结构

查看slice内部的构造:在runtime/slice.go中查看源码

1
2
3
4
5
6
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}

结构剖析,slice 共有三个属性:

  1. 指针,指向底层数组,同一个底层数组可以被多个slice同时指向,因此只要有一个slice修改,其他指向该底层数组的slice也会被修改;
  2. 长度,表示切片可用元素的个数,也就是说使用下标对 slice 的元素进行访问时,下标不能超过 slice 的长度
  3. 容量,底层数组的元素个数,容量 >= 长度。在底层数组不进行扩容的情况下,容量也是 slice 可以扩张的最大限度

对切片进行赋值的时候,实际是对表层结构体的拷贝,但是拷贝出来的结构体与原来的结构体的array指向同一个底层数组。

切片的三种状态

平常我们创建切片的方式有很多种,其实里面就包含了我们切片中的三种状态(零切片,nil切片,空切片),只是很少有人关注罢了

创建方式一 创建方式二 特点
zero slice 【任意数据类型都可以】s1 := make([]int, 10) 【指针类型】s2 := make([]*int, 10) 数组中的元素值都是对应类型的零值
nil slice var s1 []int s1 := *new([]int) 底层数组是nil
empty slice s1 := make([]int, 0) s1 := []int{} 底层数组都指向某一个特殊的内存地址(zerobase)

Golang-逃逸分析

两个命令知识

$ go tool compile -m pro_2.go 打印出编译器逃逸分析的过程,使用 -m 参数。

$ go tool compile -S pro_2.go > pro_2.S go tool compile -S pro_2.go用于反编译代码为汇编代码,这里后面使用pro_2.S表示输出到pro_2.S文件中

逃逸分析

  1. 核心:go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。
  2. 作用:go语言声称这样可以释放程序员关于内存的使用限制,更多的让程序员关注于程序功能逻辑本身。

具体流程

参考

总结

Golang中一个函数内局部变量,不管是不是动态new出来的,它会被分配在堆还是栈,是由编译器做逃逸分析之后做出的决定。

golang中的defer-recover-panic

panic

panic其实就是我们其他语言中的throw exception

recover

recover其实就是其他语言的try catch

go

golang中的iota

经常有面试官甩出个const以及iota的面试题

如下便是国内某公司的面试题:

回答输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

/**
* @Author: yirufeng
* @Email: yirufeng@foxmail.com
* @Date: 2020/10/13 6:06 下午
* @Desc:
*/


//第一个const来自于面试题
const (
i = 7
j
k
)

const (
a = 7
b
c = 3
m = 1 << iota
n
o
)

func main() {
fmt.Println(a, b, c)
fmt.Println(i, j, k, m, n, o)
}

总结

进行几点总结

  1. 不同 const 定义块互不干扰:即我们在推算这些常量值的时候只需要看当前的 const ( ) 之内的内容,不用关心之前或之后是否也用常量定义语句
  2. 所有注释行和空行全部忽略:
    1. 注意:_ 并不是一个空行,它是一个省略了标识符也省略了表达式的常量定义,这一点你需要清楚,不要大意。
  3. 没有表达式的常量定义复用上一行的表达式:例如如下的代码,j中没有写表达式,那么j是复用上一行表达式的即j = iota,但此时iota的值会为2
    1
    2
    3
    4
    const (
    i = iota
    j
    )
  4. 从第一行开始,iota 从 0 逐行加一:也就是说赋值表达式里无论是否引用了 iota,也无论引用了多少次,iota 的都会从常量定义块的第一行(注意这里不计空行和注释)开始计数,从 0 开始,逐行加一,例如上面代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const (
    i = 7 //iota = 1
    j //iota = 2
    k //iota = 3
    )

    const (
    a = 7 //iota = 1
    b //iota = 2
    c = 3 //iota = 3
    m = 1 << iota //iota = 4
    n //iota = 5
    o //iota = 6
    )
  5. 替换所有 iota:直接将表达式中的iota全部替换为我们上一步写出的值即可
go

golang中命令行参数的使用

使用os.Args获取输入的命令行参数

os.Args获取输入的命令行参数

  1. os.Args本质上是一个切片,会将命令行的内容全部读取,只是将我们输入的内容按照空格进行分割,其中第1个是执行的程序的全路径名称,
  2. 后面的为我们执行的时候命令行上的其他参数

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"os"
)

/**
* @Author: yirufeng
* @Email: yirufeng@foxmail.com
* @Date: 2020/10/6 6:00 下午
* @Desc: os.Args练习


本质上是一个切片,会将命令行的内容全部读取,只是将我们输入的内容按照空格进行分割,其中第1个是程序执行的名称,
后面的为我们执行的时候命令行上的其他参数
*/
func main() {
fmt.Println(len(os.Args))
fmt.Println(os.Args)
}

使用flag包来解析命令行参数

具体步骤

  1. StringVar,IntVar,BoolVar需要传入4个参数
    1. 第1个参数为接收值的变量的地址
    2. 第2个参数为我们使用命令行中的-后面紧跟的字符作为key
    3. 第3个参数为默认值
    4. 第4个参数为参数说明(命令设置错误时的说明)
  2. 使用flag包解析命令行参数最后一定要记得flag.Parse()之后,我们上面的设置的解析参数才会生效

到时候执行如下代码的时候,我们需要先使用go build指令进行编译,编译之后运行可执行文件的时候后面还得加上参数,这里我们应该加上./demo -h 10.26.166.14 -p 3309 -o false 如果不指定参数的时候我们将会使用程序中设置的默认值来读取

go

golang中关于文件的操作

终端读写操作note info 默认主题色,适合中性的信息 终端读写相关的文件句柄常量os.Stdin 标准输入os.Stdout 标准输出os.Stderr 标准错误以上3个都是以文件的形式存在
go