Golang相关其他分享
衔接介绍
part-1中提到了Golang中各种关键字的基本用法和一些指针的注意事项,这张主要侧重go的一些相对比较高级的操作,如果对part-1的概念点还是一知半懂的话,这一章会比较困难,所以基础很重要,当然这章其实也是基础。
推荐书籍
《go in action》,《深入解析go》,《go源码剖析》,《go语言圣经》
有的书可能老了,源码已经迭代了好几版了,需要自我鉴别,目前网上也有很多分析的例子
本章概要
1. interface 实践
methed receivers | values |
---|---|
(t T) | T and *T |
(t *T) | *T |
如上图所示,如果使用指针接收者(*T)来实现一个接口,那么只有指向那个类型的指针(*T)才能够实现对应的接口。如果使用值接收者(T)来实现一个接口,那么那个类型的值和指针(T and *T)都能够实现对应的接口。
在go中是没有继承的概念的,所以interface和组合对象这2个概念是很重要的,demo中也就该2点做了演示
2. exception 异常与捕捉(try catch? panic defer recover)
package mainimport "log"func main() { log.Println("service start") //unforgivableSystemException() protect(unforgivableSystemException) log.Println("service stop") }func unforgivableSystemException() { panic("WTF!!!!") }func protect(g func()) { //执行完g()后执行 defer func() { log.Println("done") //如果有panic的话会被recover到,相当于C#中的catch,阻止系统直接抛异常 if err := recover(); err != nil { //为什么我这里没用fmt打印,而用log,你懂得,可以异常数据收集 log.Printf("run time panic: %v", err) } }() //执行对应的函数 g() }
相对于C#,Java或其他语言中的exception而言,在go中有error和panic,日常C#开发中,我们会把人为能识别的异常通过标记一个枚举的errorCode返回,而正真的系统级异常则是exception。类似的,在golang中就大致相对着error和panic,error能更全面的控制各种人为识别的错误,在go中不推荐bool作为执行成功与否的返回值,因为bool不能完全解释false的原由到底是什么,只能表示逻辑通不过,所以error和errorCode相对应,而panic和exception相对应。
3. goroutine and channel
goroutine运行机制
图中,M是线程,P是处理器,G是goroutine,图左侧部分是M0线程上挂载了P对G队列进行处理,在执行G0时,当G0阻塞时,调度器会将M0线程与P隔离,并创建一个新的线程如图为M1来运行P上的其他G当,当阻塞在syscall上的G完成syscall调用后,G会去尝试获取一个可用的P,如果没有可用的P,那么G会被标记为runnable,之前的那个sleep的M将再次进入sleep,G在创建的时候相关执行参数会被拷贝到G的栈空间,这使得它和当前任务不再有任何关系,各自使用独立的栈空间,所以G相对于系统级多线程的上下文切换更轻量,效果更好。(这里的调度器是golang自己实现的调度器,非操作系统调度器)
channel运行机制
channel缓冲的概念如上图所示,原理上channel 其实就是一个队列加一个锁,channel中维护着队列中数据的个数,数据的大小,是否关闭状态,还配有轻量级的锁,源数据类型,收发数组的指针等等。
4. data race 和 互斥锁 以及 原子函数
以下代码文件名:main.go
package mainimport ( "fmt" "sync" "sync/atomic")var ( num int32 num2 int32 num3 int32 wg sync.WaitGroup mutex sync.Mutex //互斥锁 )func main() { wg.Add(2) go Inc() go Inc() wg.Wait() fmt.Println(num, ":", num2, ":", num3)} func Inc() { defer wg.Done() for i := 0; i < 10000; i++ { //num3++ /*原子函数,单纯效率比互斥锁高, 但是原子锁由底层硬件支持,脱离的golang机制, 虽然效率很好,但是会因为各种硬件的问题变的???(重要的字符要打3遍) 所欲对于严格要求一致的地方不适合原子函数 */ atomic.AddInt32(&num, 1) mutex.Lock() { //这对花括号只是为了标记作用域,可以不写 num2++ } mutex.Unlock() } }
data race 的出现会造成数据的不确定性,对于一个系统而言,这种不稳定因素是要极力避免的,以上的代码,我们通过“go run -race main.go” 出现以下的运行结果
================== WARNING: DATA RACE Read at 0x0000011d9e74 by goroutine 7: main.Inc() /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:27 +0x79 Previous write at 0x0000011d9e74 by goroutine 6: main.Inc() /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:27 +0x96 Goroutine 7 (running) created at: main.main() /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:20 +0x88 Goroutine 6 (running) created at: main.main() /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:19 +0x70 ================== 20000 : 20000 : 13507 Found 1 data race(s)exit status 66
而当注释掉num3++后,运行结果将会是20000:20000:0,这里就不做细致的讲解了
作者:太白菜Rennbon
链接:https://www.jianshu.com/p/9444c710d425