go基本用法
1. mod
go mod init github.com/yourusername/myproject
2.导包
函数大写:对外开放public
匿名导包_
起别名
3.数据类型
Go 中所有参数传递本质上都是值传递,map 和 slice、channel 等类型之所以表现出引用传递的特性,是因为它们内部包含指向底层数据的指针
struct:实现类,封装、继承
interface(父类指针):多态,interface{}万能类型,value, ok = arg.(string)
4.Go调度器核心设计策略详解
1. 线程复用机制(Thread Reuse)
Go调度器通过三级结构实现线程复用:
- M(Machine):操作系统线程实体
- P(Processor):逻辑处理器,含本地运行队列
- G(Goroutine):轻量级协程
工作流程:
- 空闲M会尝试绑定一个P(默认P数量=GOMAXPROCS)
- P从其本地队列(LRQ)取出G交给M执行
- 当G阻塞时,M会解绑P并进入休眠,P转而寻找其他可用M
- G执行结束后返回队列,M继续获取新任务
复用优势:
- 避免频繁创建/销毁OS线程(系统调用开销大)
- 通过P的中间层解耦M与G的关系
- 本地队列减少全局锁竞争
2. 并行执行策略(Parallelism)
Go通过三层并行设计充分利用多核:
- GOMAXPROCS控制P数量:通常设为CPU核心数
- 每个P独立调度:拥有本地运行队列,无锁访问
- 工作窃取(Work Stealing):空闲P会从其他P或全局队列偷G
并行特性:
- 真正的物理并行(非并发)
- P之间无通信开销
- 自动负载均衡
3. 协作式抢占(Cooperative Preemption)
抢占触发点:
-
显式让出点:
- 通道操作(发送/接收)
- 系统调用
- 函数调用(编译器插入检查指令)
-
时间片耗尽(Go 1.14+):
- 异步抢占信号(基于信号机制)
- 默认时间片10ms
G1运行中 -> 到达抢占点 -> 保存上下文 -> 放回队列 -> 调度器选择G2 -> 恢复G2上下文
演进历史:
- Go 1.2:只在函数调用时检查抢占
- Go 1.14:引入基于信号的全面抢占
4. 全局Goroutine管理
两级队列设计:
+------------------+ +------------------+
| 全局运行队列 |<----->| P的本地队列 |
| (GRQ) | | (LRQ) |
| [G1][G2][G3]... | | [G4][G5]... |
+------------------+ +------------------+
负载均衡策略:
-
新建Goroutine:
- 优先放入当前P的LRQ
- LRQ满时放入GRQ
-
调度选择顺序
- 本地队列(LRQ)
- 全局队列(GRQ)
- 网络轮询器(network poller)
- 从其他P窃取
设计哲学:
- 层次化:M-P-G三级结构各司其职
- 本地化:优先使用本地资源减少竞争
- 弹性:动态调整资源分配
- 高效:最小化调度开销(纳秒级切换)