书籍豆瓣链接:《Go语言学习笔记》
开始学习时间:
预计完成时间:
实际完成时间:
第1章 概述
defer FILO
结构体匿名嵌入其他类型
interface{} 接受任意对象
func (x *X) inc() {} 名称前参数是receiver
第2章 类型
变量
变量自动初始化二进制零值
:= 简短赋值,同作用域退化赋值,不同作用域定义并显式初始化
x, y = 多变量首先计算出所有右值,再依次赋值
命名
_ 可做左值,无法读取内容
常量
未使用局部变量编译错误,未使用常量没事
常量组某行不指定类型和值,与上一行非空常量右值相同
itoa 值根据下标计算
常量不分配空间,不可寻址
基本类型
引用类型
slice channel map(scm)
new 分配零值内存,返回指针
make([]int, 0, 10) make会调用目标类型创建函数,内存分配并初始化,引用类型必须使用make
类型转换
强制要求使用显示类型转换
转换的目标类型是指针,单向通道或无返回值函数,必须使用括号
第3章 表达式
运算符
二元操作符 除位移,两个操作数类型必须相同
++ – 只能后置,是语句不是表达式
指针支持相等运算,不能做加减和类型转换
初始化
{} 复合类型初始化,使用{}
流控制
if switch 支持多语句,根据最右边的语句判断,如为空默认true
for i,x := range,i和x分别是下标和值
range会复制目标数据,如想操作元数据,可以用数组指针或切片类型
第4章 函数
定义
不支持同名函数重载
不支持命名嵌套定义
不支持默认参数
函数返回局部变量指针是安全的,通过逃逸分析决定在堆上分配内存
参数
不支持默认参数,不支持命名实参
都是值拷贝传递
指针参数会延长目标对象生命周期,可能导致分配到堆上
如果复制成本很高,或需要修改原对象,使用指针更好,否则尽可能使用不可变对象
变参 a …int复制的是切片,并不是底层数组
返回值
命名返回值和参数一样,当做局部变量使用,由return隐式返回
匿名函数
闭包是函数和其引用的环境的组合体
闭包通过指针引用环境变量,可能导致生命周期延长,甚至被分配到堆内存
闭包有延迟求值特性
延迟调用
直到当前函数执行结束前才被执行,常用于资源释放、接触锁定以及错误处理
多个延迟注册按FILO执行,可以想象一个调用栈,先进后厨
return和panic触发延迟调用
错误处理
panic会立即中断当前函数流程,执行defer,defer中panic不会影响后续defer
defer中recover可捕获并返回panic提交的错误对象,recover必须在defer中执行,需要转型才能获取具体信息
连续调用panic,仅最后一个会被recover捕获,recover之后panic可被再次捕获
第5章 数据
字符串
string采用uft8编码,是变宽字符序列,len返回字节数
string具有不变性,字符串拷贝和切片都需要分配新内存
`反引号定义不做转义的raw string
要修改string,需要转换为可变类型[]rune和[]byte
string接受到[]rune类型转换,将utf8编码的字符串解码为unicode字符数组
byte 占用1个节字,所以它和uint8类型本质上没有区别,它表示的是ACSII表中的一个字符
rune 4个字节,共32位比特位,所以它和uint32本质上也没有区别。它表示的是一个Unicode字符
数组
切片
slice引用底层数组,本身是个只读对象,x[low,high,cap]是半开区间
cap表示切片引用底层数组片段的真实长度,len=high-low表示可读的写元素数量
make([]T, len, cap)和make([]T, cap)[:len]创建一个容量cap的匿名数组变量,返回一个slice,底层数组初始化为零值
make允许运行时动态指定数组长度
slice代替数组传参可避免复制开销
字典
使用make或者初始化表达语句创建map,否则是nil map
map的value的成员不可直接修改
运行时会对字典并发操作进行检测,某个任务对字典写操作,其他任务就不能读,写或删除,否则panic
map要使用sync.RWMutex同步
结构
struct 只有在所有字段都可比较,才可以比较
可以使用指针直接操作struct里的字段,但不可以是多级指针
struct{}可用作channel元素类型,用于事件通知
长度为0的对象都指向runtime.zerobase
内存布局 struct个字段在相邻地址空间按定义顺序排列。引用类型、字符串和指针,struct内存中只包含头部(ptr,len,cap)。其他字段以及匿名字段都包含在struct中
第6章 方法
方法
方法和函数的区别在于,方法有receiver
receiver可以是基础类型或指针类型,如果一个类型本身是指针,不可以成为receiver
receiver是基础类型,调用时对象实例会被复制
receiver形参是T,实参是*T,编译器会隐式取变量地址
receiver形参是*T,实参是T,编译器会隐式解引用,取到指针实际变量
适合使用*T的情况:
- 修改实例状态
- 大对象减少复制成本
- 包含Mutex,避免因复制造成锁操作无效
- 其他未知情况
匿名字段
可以像访问匿名字段成员那样调用其方法
struct自身方法,可以override匿名字段方法
方法集
类型*T的方法集包括所有receiver T+ *T的方法
匿名嵌入*S,T方法集包括所有receiver S+ *S的方法
匿名嵌入S或*S,*T方法集包含所有receiverS+ *S的方法
Go倾向于组合优于继承,将模块分解成相对独立的更小单元,处理不同方面的需求,最后以匿名嵌入方式组合到一起,共同实现对外接口
表达式
第7章 接口
定义
Go接口实现机制很简洁,只要目标类型方法集合内包含接口声明的全部方法,就被视为实现了该接口,无须做显示声明
因此可以先实现类型,而后再抽象出所需接口
interface通常以er作为名称后缀
interface{}类似于跟类型object,可被赋值为任何类型对象
执行机制
interface包含两个指针,类型指针itab和值指针data,都为nil时,接口值是nil
1 | type iface struct { |
将对象赋值给接口变量,会复制该对象,而且不可寻址是unaddressable的
解决方法是将对象指针赋给接口,那么接口内存储的就是指针的复制品
类型转换
.(type)类型推断可以将接口变量还原为原始类型,用来推断是否实现了某个更具体的接口类型
技巧
第8章 并发
并发的含义
并发与并行
并发 逻辑上具备同时处理多个任务的能力
并行 物理上在同一时刻执行多个并发任务
协程概念
协程在单个线程上通过主动切换实现多任务并发,减少阻塞和线程切换开销,本质上还是串行的
进程、线程和协程
多进程 用来实现分布式和负载均衡,减轻单进程垃圾回收压力
多线程 LWP抢夺更多的处理器资源
协程 用来提高处理器时间片利用率
python由于GIL,只能并发不能并行,使用多进程+协程架构
并发模型
-
Actor模型
每一对Actor之间,都有一个“MailBox”来进行收发消息。消息的收发是异步的
-
CSP模型
Communicating Sequential Process, 通信序列进程
使用定义的channel进行收发消息。消息的收发是同步的(也可以做成异步的,但是一个有限异步)
go routine
关键字go并非执行并发操作,而是创建一个并发任务单元,添加到系统队列中
每个go协程有自己的协程栈,初始大小2kb(线程栈MB级)
go协程没有优先级,无法获取编号,没有局部存储TLS,返回值也会被丢弃
runtime参数
GOMAXPROCS 设置线程数,默认与CPU核数相同
NumCPU 机器CPU核数
Local Storage
使用map做局部存储容器,建议做同步处理,因为会做并发读写检查
routine调度
wait 进程退出不会等待任务结束,可用channel或sync.WaitGroup
Gosched 释放线程去执行,当前任务被放回队列
Goexit 终止当前任务,运行时确保所有已注册延迟调用被执行,期间不会引发panic。main中调用,会等待其他任务结束,然后让进程直接崩溃
通道
Go允许使用全局变量、指针、引用类型这些非安全内存共享操作
Go鼓励使用CSP,以通信代理内存共享
同步和异步
make(chan T) 必须要有配对操作的goroutine出现,否则加入等待队列,直到另一方出现后唤醒
make(chan T, N) 发送方要有空槽可写,接收方要有数据可读或者管道关闭,否则加入等待队列
cap() len() 返回缓冲区大小和当前已缓冲数量
收发
close©和<-c配对,用于同步
x, ok:=<-c 根据ok判断通道是否关闭
for x:=range c {} 循环收消息,直到通道关闭
nil通道不能关闭,收发都会阻塞
closed通道,写panic,读返回以缓冲数据或零值,重复关闭panic
单向
c:=make(chan int)
var send chan<- int=c
var recv <-chan int=c
close操作不能用于接收端
选择
select语句,随机选择一个可用通道做收发操作,break退出
模式
性能
将发往通道的数据打包,减少传输数据 make(chan [block]int, bufsize)
资源泄漏
goroutine处于发送或接收阻塞状态,但一直未被唤醒,垃圾回收器并不收集此类资源,会在等待队列里休眠,造成资源泄漏
使用runtime.GC强制垃圾回收也没用
同步
通道倾向于解决逻辑层次的并发处理架构,锁用来保护局部范围内的数据安全
sync.Mutex互斥锁,不支持递;sync.RWMutex读写锁
第9章 包结构
工作空间
导入包
组织结构
依赖管理
第10章 反射
类型
反射让我们能在运行期探知对象的类型信息和内存结构
Go对象头部没有类型指针,反射操作所需的全部信息都源自接口变量
面对类型时,Type表示真实类型(静态类型),Kind表示基础结构(底层类型)
Elem返回指针、数组、切片、字典(值)或通道的基类型,如果是其他类型会panic
值
reflect.ValueOf(a)返回一个Value结构体,Value获取对象实例
CanAddr和CanSet实际上读取的是Value的flag字段,ValueOf返回的对象都是false
使用Elem方法返回可寻址对象(CanAddr),Elem方法只接受kind为interface和ptr的a
能探知当前包和外包的非导出结构成员(CanAddr),但只能修改导出成员(!CanSet)
可以使用Inteface().(.T)进行类型推断
方法
1 | v := reflect.ValueOf(&a) |
构建
1 | func add(args []reflect.Value) (result []reflect.Value) {} |
性能
第11章 测试
单元测试
性能测试
代码覆盖率
性能监控
第12章 工具链
第13章 准备
第14章 引导
第15章 初始化
第16章 内存分配
runtime,虚拟机的一种,一般指进程级别的虚拟机。运行时是指一个程序在运行(或者在被执行)的依赖