- map的原理
- 字典[map]:
初始化: var amap map[string]string, 如果需要赋值,需要先make 一下
testMap := make(map[string]string)
Go中的字典是引用类型,但Golang中是没有引用传递的,均为值传递。这意味着传递的是数据的拷贝,所以map 也存在线程安全的问题
底层结构是hmap,hmap 的结构体里有一个buckets,他是一个指针,指向了一个bucket的结构体,bucket可以看错一个链表,bucket 里存放着k&&v 的数组,以及指向下一个bucket的指针。
所以当hmap收到了key,会进行hash运算,然后基于运算结果找到对应的bucket里的数组,然后找到对应的Key
- make和new的区别
- new:用来初始化一个对象,并且返回该对象的首地址.其自身是一个指针.可用于初始化任何类型
- make:返回一个初始化的实例,返回的是一个实例,而不是指针,其只能用来初始化:slice,map和channel三种类型
- go的GC 机制
- 标记-清除模式: 从根变量开始遍历所有引用的对象,引用的对象标记为"被引用",没有被标记的进行回收。
- defer的执行顺序
多个defer 执行顺序类似堆栈,后进先出,defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。
如果函数的返回值是无名的(不带命名返回值),则go语言会在执行return的时候会执行一个类似创建一个临时变量作为保存return值的动作,而有名返回值的函数,由于返回值在函数定义的时候已经将该变量进行定义,在执行return的时候会先执行返回值保存操作,而后续的defer函数会改变这个返回值(虽然defer是在return之后执行的,但是由于使用的函数定义的变量,所以执行defer操作后对该变量的修改会影响到return的值
package main import ( "fmt" ) func haveReturnName() (a int) { defer func() { a++ }() return a } func haveNoReturnName() (int) { var b int defer func() { b++ }() return b } func main() { fmt.Println(haveReturnName()) fmt.Println(haveNoReturnName()) }
- recover && panic
go没有 try, expect,finnally 之类的异常捕捉方法,通过defer 判断是否捕捉到recover,如果没有则返回nil,如果recover捕获到panic,则处理。
package main import "fmt" func prTest() { defer func() { if r := recover(); r != nil { fmt.Printf("panic recover! %v",r) } }() for i := 0; i < 5; i++ { if i == 3 { panic(fmt.Sprintf("%v",i)) } fmt.Println(i) } } func main() { prTest() }
- golang 如何处理大文件?
- 切片(数据流),适合二进制文件,没有换行符的,非常大的http返回
package main import ( "os" "fmt" ) func readBigFile(fileName string) { f, err := os.Open(fileName) if err != nil { fmt.Println("can't opened this file") panic(err.Error()) } defer f.Close() s := make([]byte, 1024) for { switch nr, _ := f.Read(s[:]); true{ case nr > 0: fmt.Println(nr) case nr <= 0: //EOF return } } } func main() { readBigFile("loutong.go") }
- 逐行读取
package main import ( "bufio" "fmt" "os" "io" ) func readBigFile(fileName string) { f, err := os.Open(fileName) defer f.Close() if err != nil { panic(err.Error()) } buf := bufio.NewReader(f) for { c, _, err := buf.ReadLine() fmt.Printf("%s\n",c) if err == io.EOF { return } if err != nil && err != io.EOF { fmt.Printf("读取错误") return } } } func main() { readBigFile("loutong.go") }
- go的context作用
- 批量协程的关闭
- 协程的超时处理
package main import ( "context" "time" "fmt" ) func ctxCancelThing() { ctx, cancel := context.WithCancel(context.Background()) go doSomething(ctx) time.Sleep(10 * time.Second) cancel() } func ctxTimeoutThing() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5 * time.Second)) go doSomething(ctx) time.Sleep(10 * time.Second) cancel() } func doSomething(ctx context.Context) { for { time.Sleep(1 * time.Second) select { case <-ctx.Done(): fmt.Printf("done") return default: fmt.Printf("work") } } } func main() { ctxCancelThing() ctxTimeoutThing() }
谈谈go的协程泄露
一个原则,在不知道如何关闭一个协程的情况下,永远不要启动一个goroutine。select 和 switch的区别
select 不带判断条件,主要是用于判断IO操作的,确切的说,应该是一个面向channel的IO操作go语言中布尔类型的缺省值是什么?
False