其他分享
首页 > 其他分享> > 字节后端训练营——Go由浅入深

字节后端训练营——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