Go基础
interface
interface变量
interface,就是一组抽象方法的集合,里面到底能存什么值呢?如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个interface的任意类型的对象。
我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:
Comma-ok断言
if value, ok := interface.(T); okswitch测试
switch value := element.(type) {
case int:
fmt.Printf("is an int, value is %d\n", index, value)
case ...
}
// `element.(type)`语法不能在switch外的任何逻辑里面使用空interface
所有的类型都实现了空interface。有点类似于C语言的void,在我们需要存储任意类型的数值的时候比较有用。
interface函数参数
可通过定义interface参数,让函数接受各种类型的参数。如对于fmt.println:
则实现此接口的type即可进行print。实现了error接口的对象(即实现了Error() string的对象),使用fmt输出时,会调用Error()方法。
如何确保某个类型实现了某个接口的所有方法呢?一般可以使用var _Person = (*Student)(nil)进行检测,如果实现不完整,编译期将会报错。
接口不仅能作为函数的参数,还能作为结构体的属性。
面向对象
为什么需要interface? 如若一个类耦合太多的功能,内聚度就不够。不如按功能分出接口,使得多个业务代码不用耦合在一个类里。这是开闭原则的体现。([O]一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。在修改需求的时候,应该尽量通过扩展来实现变化)
依赖倒转原则(D):class 应该依赖接口和抽象类而不是具体的类和函数。
反例

正例

我们在设计一个系统的时候,将模块分为3个层次,抽象层、实现层、业务逻辑层。将抽象层的模块和接口定义出来,这里就需要了interface接口的设计。
Channel
不要通过共享来通信,而要通过通信来共享。
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。
Select
Go里面提供了一个关键字select,通过select可以监听channel上的数据流动。默认是阻塞的。当多个监听的channel都准备好的时候,随机选择一个处理。
GC
追踪式
程序中运行\所需的数据,一定是栈、数据段这些根节点追踪得到的数据,因此通过可达性分析,三色抽象如下:
白色:init
灰色:直接追踪到的root节点,追踪完成后为黑色
黑色:存活(可达)数据,追踪到的节点标记为灰色
可见,白色的都是垃圾。基于此:
标记-回收算法:简单,会导致较多的堆内存垃圾;根据
BiBOP(Big Bag of Pages)的思想,可以规格化分配内存块、统一管理相同内存大小的块。标记-整理算法:移动非垃圾数据,使其紧凑地放在内存中。拷贝开销大。
复制式回收:分区
FromTo,程序执行时使用From,垃圾回收时复制有用的数据到To,回收From,并交换From和To空间。降低了堆内存可用量。分代回收:基于弱分代假说(参考JVM),数据划分为新生代和老年代,对新生代和老年代执行不同的GC策略。
引用计数:每次对数据对象进行计数,此方案维护开销较大,且循环引用没回收到。
增量式垃圾回收
在三色抽象中,如果不stop-the-world,GC会误判黑色对象对白色对象的新引用(同时无灰色对象引用该白色对象)。提出两个不变式:
强三色不变式:不允许黑色对象对白色对象的新引用
弱三色不变式:如果黑色对象对白色对象新引用,也存在灰色对象引用该白色对象。 通过读写屏障维持不变式。

混合屏障
插入写屏障:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;
删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
Go V1.8版本引入了混合写屏障机制(hybrid write barrier),避免了对栈re-scan的过程,极大的减少了STW的时间。结合了两者的优点:
GC时,栈上的新旧对象都标记为黑色,被删除/添加的对象标记为灰色。
多核并行垃圾回收

Goroutine与协程
大佬的观点:Goroutine是用户态线程,不是协程。
协程:可暂停和恢复执行的procedure(函数)。有这么一些理解:
本质(核心)是直接调度,也就是可以控制让出和恢复。不是用户态线程(运行的载体)。
goroutine是编程语言内部进行调度的,不支持手动控制执行流(yield)。协程可以作为IO的载体,以解决IO的不确定性。既然是解决不确定性问题,则可以理解为异步框架,优势是好写。
coroutine可理解为【协函数】,比如C++20的co_wait等待事件。
控制流体操
输出结果
当 panic 被触发时,控制权就被交给了 defer 。后面的代码不执行。
Notes
对等C中void*的数据类型,就是unsafe.Pointer。
最后更新于
这有帮助吗?