字节后端训练营——Go由浅入深
作者:互联网
\(\color{blue}{容器:数组、切片、映射}\)
\(\color{purple}{Array}\)
var num1 [10]int
var num2 [5]int
var num3 [5]int32
(1)三种各不相同,只有元素类型和长度都完全相同,数组类型才相同
var num1 [3]int
num2 := [3]int{1,2,3}
num3 := [...]int{1,2,3}
(2)三种初始化方法
(3)数组类型是值类型,不是引用类型(函数传递后只操纵数组的备份)
\(\color{purple}{Slice}\)
type Slice struct{
Data unsafe.Pointer
Len int
Cap int
}
(1)切片定义的几种方法
var aa []int
aa = make([]int,4)
bb := make([]int,4,6)
var num = [10]int{0,1,2,3,4,5,6,7,8,9}
slice1 := num[2:5:8] // [2,3,4],len = 3,cap = 6
slice2 := num[2:5] // 这里要注意slice之间的互相影响
slice3 := num[2:]
slice := num[:5]// [0,1,2,3,4]
slice := num[:]
(2)切片拷贝
slice2 := make([]int ,3)
copy(slice2,slice1[1:4])
(3)常用函数
// append 会自动扩容,但会影响性能,通常来说1024以下扩容翻一倍,以上增加1/4
slice1 := make([]int,0,2)
slice1 = append(slice1,1,2,3)
// range 遍历 事实上是一种拷贝,并不影响原来的切片
for _,n := range slice {
n = n * 2
}
\(\color{purple}{Map}\)
映射的构成,和映射键值对的插入删除
students := make(map[string]int)
students["Jack"] = 10
delete(students , "Jack")
实现set:
map[string]struct{}
\(\color{purple}{Struct}\)
结构体的定义
type Gender int
struct{
Female Gender = 0
Male Gender = 1
}
type Person struct {
Name string `json : "person_name" `
Age int32
Sex Gender
}
方法和函数的区别
func(p Person) ShowInfo(){
if p.Sex == Female {
fmt.Println("Her name is Alice")
}else {
fmt.Println("His name is Daniel")
}
}
结构体的初始化法方法
这里注意到无论是指针还是结构体都是用.
// 1.直接初始化
// 2.使用&返回指针
// 3.使用new,返回结构体指针
\(\color{purple}{Interface}\)
描述某个类型有哪些方法。或者说一个接口类型,定义了一个方法集
空接口可以被认为是其他语言的“any”类型(Println()为什么什么东西都能打?)
类型断言
func main(){
var x interface{} = 123
// 1
n,ok := x.(int)
fmt.Println(n,ok) // 123 true
// 2
a,ok := x.(float64)
fmt.Println(a,ok) // 0 false
// 3
a := x.(float64) //产生一个panic
}
\(\color{purple}{Goroutine}\)
func main(){
fmt.Println("主协程开始")
go func(){
time.sleep(1*time.Second)
fmt.Println("print log")
time.Sleep(1*time.Second)
}
fmt.Println("主协程结束")
}
这里注意到主协程一旦结束其他都结束
JMP模型
协程与线程最好是N:M的关系
协程的调度器的设计策略(如何减少开销)
(1)复用线程
(2)利用并行
(3)抢占:限制协程执行时长
(4)全局协程队列:链表实现
常用的同步控制机制:WaitGroup
func main(){
var wg sync.WaitGroup;
for i := 0;i < 3;i++{
wg.Add(1)
go func(i int){
t := rand.Intn(3)
time.Sleep(time.Duration(t)*time.Second)
wg.Done()
}(i)
}
wg.wait()
}
\(\color{purple}{Channel}\)
并发模型CSP
管道的操作有:读写关闭
ch := make(chan string)
a = <-ch
ch <- "Hello" // 写一个已经关闭的channel就会引发panic
管道分类:无缓冲的管道(没有额外的拷贝,读在等写)和有缓冲的管道(会发生额外的拷贝,缓冲区的最大是65535)
交替打印A和B,(1)B发出信号(2)A打印,向B传信号(3)B...(4)打印完结束向主协程发信号
package main
import "fmt"
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
ch3 := make(chan string)
go printA(ch1, ch2)
go printB(ch1, ch2, ch3)
<-ch3
}
func printA(ch1, ch2 chan string) {
for i := 0; i < 100; i++ {
<-ch2
fmt.Println(i, "A")
ch1 <- "print A"
}
}
func printB(ch1, ch2, ch3 chan string) {
ch2 <- "begin"
for i := 0; i < 100; i++ {
<-ch1
fmt.Println(i, "B")
if i != 99 {
ch2 <- "print B"
} else {
ch3 <- "end"
}
}
}
\(\color{purple}{几项最佳实践}\)
(1) init()执行晚于变量的初始化而早于main()函数
(2) 关于切片追加,append的结果最好保留在相同的切片,如果要用新的切片,建议使用copy
func main(){
x := []int{}
x = append(x,0)// 1 1
x = append(x,1)// 2 2
y := append(x,3)//3 4
z := append(x,4)//3 4 它影响到了y
fmt.Println(x,y,z) // 都是[0 1 2] [0 1 2 4] [0 1 2 4]
}
(3)关于切片的构建
如果能预知切片的大小最好是预先设置,避免append的扩容带来的开销
size := 3
data := make([]int,size) // 使用data := make([]int,0,size)则可以,这样size只是cap
data = append(data,2) // [0,0,0,2]
(4)关于指针
Golang中两种指针1.限制型指针(只能寻址,但不能加减地址)2.无限制指针(unsafe.pointer)(可以任意写内存)
什么时候用指针(1.需要修改,不用的时候建议不用指针 2.容器占内存较大(提高性能)(这里注意到容器较小时因为有逃逸分析指针未必节省时间))
(5)关于panic
可以主动使用panic,常常和defer一起使用
func main(){
go some()
go func(){
defer func(){
if err := recover();err != nil{
fmt.Println("panic is recovering")
}
}()
panic("This is a panic")
}
time.Sleep(2*time.Second)
fmt.Println("end")
}
标签:由浅入深,int,训练营,Println,func,Go,fmt,append,make 来源: https://www.cnblogs.com/OceanCT/p/15843815.html