Package initialization—variable initialization and the invocation of init functions—happens in a single goroutine, sequentially, one package at a time. A package is initialized only once, even if imported from multiple packages. Given the list of all packages, sorted by import path, in each step the first uninitialized package in the list for which all imported packages (if any) are already initialized is initialized. Initialization proceeds until all packages are initialized.
该规范描述的动态选择算法在实现上被优化为预计算顺序+直接遍历,分为四个阶段:
flowchart TB
subgraph Load["阶段1: 包加载"]
L1["从 main 包开始"] --> L2["DFS 后序遍历依赖<br/>cmd/go/internal/load"]
end
subgraph Compile["阶段2: 编译"]
C1["解析 AST"] --> C2["生成 init 函数<br/>cmd/compile/internal/pkginit"]
C2 --> C3["生成 pkg..inittask<br/>含 R_INITORDER 重定位"]
end
subgraph Link["阶段3: 链接"]
K1["收集所有 inittask"] --> K2["拓扑排序+字典序<br/>cmd/link/internal/ld"]
K2 --> K3["输出 go:main.inittasks"]
end
subgraph Run["阶段4: 运行"]
R1["遍历 []*initTask"] --> R2["执行 init 函数<br/>runtime/proc.go"]
end
Load --> Compile --> Link --> Run
package test-cycle
imports test-cycle/cycle_a from main.go
imports test-cycle/cycle_b from cycle_a.go
imports test-cycle/cycle_a from cycle_b.go: import cycle not allowed
var message stringmessage = "hello world"var x interface{} // x is nil and has static type interface{}var v *T // v has value nil, static type *Tx = 42 // x has value 42 and dynamic type intx = v // x has value (*T)(nil) and dynamic type *T
uint8 the set of all unsigned 8-bit integers (0 to 255)uint16 the set of all unsigned 16-bit integers (0 to 65535)uint32 the set of all unsigned 32-bit integers (0 to 4294967295)uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)int8 the set of all signed 8-bit integers (-128 to 127)int16 the set of all signed 16-bit integers (-32768 to 32767)int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)float32 the set of all IEEE 754 32-bit floating-point numbersfloat64 the set of all IEEE 754 64-bit floating-point numberscomplex64 the set of all complex numbers with float32 real and imaginary partscomplex128 the set of all complex numbers with float64 real and imaginary partsbyte alias for uint8rune alias for int32
简洁直观。
和C一样,还有些类型和具体的实现有关:
uint either 32 or 64 bitsint same size as uintuintptr an unsigned integer large enough to store the uninterpreted bits of a pointer value
// 1. 直接包含自身type T [3]T // 错误:数组包含自己// 2. 结构体包含自身type S struct { x S // 错误:结构体包含自己}// 3. 间接递归(只有数组/结构体)type A [3]struct { x A // 错误:数组→结构体→数组,只有数组和结构体}
下列合法:
// 1. 指针介入type Node struct { next *Node // 允许:指针不是数组/结构体}// 2. 切片介入type Tree struct { children []Tree // 允许:切片不是数组/结构体}// 3. 接口介入type List struct { head interface{ Next() *List } // 允许}// 4. Map 介入type Graph struct { edges map[string]*Graph // 允许}
// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4struct { T1 // field name is T1 *T2 // field name is T2 P.T3 // field name is T3 *P.T4 // field name is T4 x, y int // field names are x and y}
内嵌字段的方法”提升“,比如:
type Inner struct {}func (i Inner) Method() {}type Outer struct { Inner // 嵌入字段(匿名)}var o Outero.Method() // 合法!Inner.Method 被"提升"到 Outer
chan T // can be used to send and receive values of type Tchan<- float64 // can only be used to send float64s<-chan int // can only be used to receive ints
复杂例子:
chan<- chan int // same as chan<- (chan int)chan<- <-chan int // same as chan<- (<-chan int)<-chan <-chan int // same as <-chan (<-chan int)chan (<-chan int)
Two array types are identical if they have identical element types and the same array length.
Two slice types are identical if they have identical element types.
Two struct types are identical if they have the same sequence of fields, and if corresponding pairs of fields have the same names, identical types, and identical tags, and are either both embedded or both not embedded. Non-exported field names from different packages are always different.
Two pointer types are identical if they have identical base types.
Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match.
Two interface types are identical if they define the same type set.
Two map types are identical if they have identical key and element types.
Two channel types are identical if they have identical element types and the same direction.
Two instantiated types are identical if their defined types and all type arguments are identical.
类型和值的关系
类型相同或者实现了你才能赋值。
我在你能表示的范围内。
比如:
var a int8 = 300 // 超出了
方法集
每个类型都有一个与它相关的方法集(可能为空)。
已定义类型 T 的方法集由所有以接收器类型 T 声明的方法组成。
指向已定义类型 T 的指针(其中 T 既不是指针也不是接口)的方法集,是以接收器 *T 或 T 声明的所有方法的集合。
Types:
any bool byte comparable
complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
Constants:
true false iota
Zero value:
nil
Functions:
append cap clear close complex copy delete imag len
make max min new panic print println real recover
var i intvar U, V, W float64var k = 0var x, y float32 = -1, -2var ( i int u, v, s = 2.0, 3.0, "bar")var re, im = complexSqrt(-1)var _, found = entries[name] // map lookup; only interested in "found"
type Pair[A, B any] struct { a A b B}func (p Pair[A, B]) Swap() Pair[B, A] { … } // receiver declares A, Bfunc (p Pair[First, _]) First() First { … } // receiver declares First, corresponds to A in Pair
Tip
方法名并不是一个有效的变量,不可以直接赋值,它必须依附于类型,而函数就可以
Expressions
Composite字面量
struct字面量
type Point3D struct { x, y, z float64 }// 都是合法的origin := Point3D{}p1 := Point3D{1.1,1.2,1.3} // 省略key时,必须所有字段按顺序赋值p2 := Point3D{y: -4, z: 12.3} // 写key时,可以省略field,可以不按key顺序
[...]Point{{1.5, -3.5}, {0, 0}} // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}[][]int{{1, 2, 3}, {4, 5}} // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}[][]Point{{{0, 1}, {1, 2}}} // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}map[string]Point{"orig": {0, 0}} // same as map[string]Point{"orig": Point{0, 0}}map[Point]string{{0, 0}: "orig"} // same as map[Point]string{Point{0, 0}: "orig"}type PPoint *Point[2]*Point{{1.5, -3.5}, {}} // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}[2]PPoint{{1.5, -3.5}, {}} // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}
s := a[1:5:7] // 长度为4,容量为6s[4] // panic,越界了s[:6] // 结果为{2,3,4,5,6,7}
Type assert
断言为更具体的类型,不会改变内存表达。
比如:
var x interface{} = 1i, ok := x.(int) // if ok, i断言为intvar i, ok any = x.(int) // 左边的这个any不影响实际的类型,i依然int,ok为bool,不过静态编译时还是会被当作any :(reflect.TypeOf(i) // int
断言为具体类型 vs 断言为接口类型
type I interface { m() }func f(y I) { s := y.(string) // illegal: string does not implement I (missing method m) r := y.(io.Reader) // r has type io.Reader and the dynamic type of y must implement both I and io.Reader …}
a := 1f := func() int { a++; return a }x := []int{a, f()} // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specifiedm := map[int]int{a: 1, a: 2} // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specifiedn := map[int]int{a: f()} // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified
switch i := x.(type) {case nil: printString("x is nil") // type of i is type of x (interface{})case int: printInt(i) // type of i is intcase float64: printFloat64(i) // type of i is float64case func(int) float64: printFunction(i) // type of i is func(int) float64case bool, string: // type list, i has the type of the expression in the TypeSwitchGuard printString("type is bool or string") // type of i is type of x (interface{})default: printString("don't know the type") // type of i is type of x (interface{})}
var prints []func()for i := 0; i < 5; i++ { prints = append(prints, func() { println(i) }) i++ // 再次改变这个block中的i,函数执行的时候,i是这里+1后的值}for _, p := range prints { p()}// 1 3 5
如果不是重新定义,那么所有的函数都引用了同一个变量,最后结果就是6 6 6。
Note
js中也有个类似的陷阱,关于var和let的:
for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // 输出:3, 3, 3 }, 100); }
编译后本质是执行BadIterator,这个yield参数,由编译器构建,它本质是对range循环体的封装 ,执行一次yield就是执行一次循环体,如果循环体终止,比如break,yield返回false;当yield返回false后,再次调用yield就会panic: runtime error: range function continued iteration after function for loop body returned false。
所以这完全没有任何魔法。
yield的函数签名:
yield func() bool
yield func(V) bool
yield func(K, V) bool
func BadIterator(yield func(int) bool) { for i := 1; i <= 5; i++ { fmt.Printf("--- 迭代器内部:尝试产生数字 %d ---\n", i) res := yield(i) fmt.Printf("--- 迭代器内部:yield(%d) 的返回值是 %v ---\n", i, res) if !res { fmt.Println("!!! 检测到外部 break,但我拒绝退出,我要再次尝试 yield !!!") // 这里不 return,强行继续循环进入下一次 yield // return } }}func RunBadIterator() { for v := range BadIterator { fmt.Printf("外部循环:接收到 %d\n", v) if v == 1 { fmt.Println("外部循环:执行 break") break } }}
var a []intvar c, c1, c2, c3, c4 chan intvar i1, i2 intselect {case i1 = <-c1: print("received ", i1, " from c1\n")case c2 <- i2: print("sent ", i2, " to c2\n")case i3, ok := (<-c3): // same as: i3, ok := <-c3 if ok { print("received ", i3, " from c3\n") } else { print("c3 is closed\n") }case a[f()] = <-c4: // same as: // case t := <-c4 // a[f()] = tdefault: print("no communication\n")}for { // send random sequence of bits to c select { case c <- 0: // note: no statement, no fallthrough, no folding of cases case c <- 1: }}select {} // block forever
Return statements
可以返回多个,可以具名。
func complexF1() (re float64, im float64) { return -7.0, -4.0}func complexF3() (re float64, im float64) { re = 7.0 im = 4.0 return // 不需要指定了,返回的变量赋值了,但是这个return不能省略,必须有显示的terminating语句}
不管是形参,还是返回参数,本质都是函数体作用域的本地变量。
Defer
defer函数,类似finally。
函数在出栈前,一定会调用defer,除非没有找到声明的defer(还没执行到就return了):
return返回之前,return不是一个原子操作,分为赋值(分配),返回,返回才会出栈
出现panic,会出栈
没有return,函数体执行完时
defer是有可能会修改返回值的,特别是具名的情况下:
func Anonymous() int { x := 5 defer func() { x = x + 10 // 这里改的是局部变量 x,不是 return 已经存好的备份 }() return x}func Named() (x int) { defer func() { fmt.Println("defer看到的结果是:", x) // 输出 5 x = 10 // 10赋值给x, 修改结果 }() return 5 // 5赋值给x}
OuterLoop: for i = 0; i < n; i++ { for j = 0; j < m; j++ { switch a[i][j] { case nil: state = Error break OuterLoop case item: state = Found break OuterLoop } } }
Continue statements
ContinueStmt = "continue" [ Label ] .
除了针对for,直接下一次迭代而不是终止,其他和break相同。
Goto statements
GotoStmt = "goto" Label .
为了防止滥用,有很多限制:
goto必须在同一个function
不能跨block,更严格的限定
goto下面不能申明新的变量,因为这样会跳过变量的初始化,导致可能的错误调用
goto L // BAD v := 3L:// -----------if n%2 == 1 { goto L1 // BAD}for n > 0 { f() n--L1: f() n--}
classDiagram
class G {
+stack stack
+sched gobuf
+status uint32
+m *m
+goid uint64
}
class M {
+g0 *g
+curg *g
+p *p
+id int64
}
class P {
+status uint32
+m *m
+runq[256] guintptr
+mcache *mcache
+id int32
}
class schedt {
+runq gQueue
+runqsize int32
+pidle pList
+midle mList
}
G "n" --> "0..1" M : curg
M "n" --> "0..1" P : 绑定
P "n" <-- "1" schedt : 全局调度器
P "1" *-- "1" mcache : 拥有
P "1" o-- "n" G : runq持有引用
G状态流转图
展示 Goroutine 的完整生命周期。
stateDiagram-v2
[*] --> _Gidle : 创建
_Gidle --> _Grunnable : 放入队列
_Grunnable --> _Grunning : M取出执行
_Grunning --> _Grunnable : 抢占或Gosched
_Grunning --> _Gwaiting : channel或网络阻塞
_Grunning --> _Gsyscall : syscall进入
_Gsyscall --> _Grunning : syscall返回获P
_Gsyscall --> _Grunnable : syscall返回无P
_Gwaiting --> _Grunnable : 唤醒
_Grunning --> _Gdead : 执行完毕
_Gdead --> [*] : 回收或复用
note right of _Gwaiting
存放位置
channel.recvq/sendq
netpoller
timer heap
end note
note right of _Gsyscall
触发syscall的情况
文件I/O: open/read/write
系统调用: time/getpid/exec
CGO调用
特点: G跟着M
P可能被handoff
end note
note right of _Grunnable
存放位置
P.runq本地队列
sched.runq全局队列
end note
note left of _Grunning
让出CPU方式
Gosched: G主动让出
抢占: sysmon强制
超过10ms触发
end note
stateDiagram-v2
[*] --> _Pidle : 创建
_Pidle --> _Prunning : M绑定
_Prunning --> _Pidle : 无G可执行
_Prunning --> _Pgcstop : GC开始
_Pgcstop --> _Pidle : GC结束
note right of _Pidle
P空闲等待:
sched.pidle列表
等待M绑定
end note
note right of _Prunning
P绑定M执行G
runq有待执行G
syscall时P仍为_Prunning
但可通过M.curg.status
判断M是否在syscall
end note
状态
含义
_Pidle
空闲,等待 M 绑定
_Prunning
正在执行,绑定了 M
_Pgcstop
GC 期间停止
_Pdead
不再使用(GOMAXPROCS 缩减时)
调度循环流程图
展示 M 获取 G 并执行的完整调度循环。
flowchart TD
A[schedule循环开始] --> B{schedtick%61==0<br/>全局队列有G?}
B -->|是| C[从全局队列取G<br/>确保公平性]
B -->|否| D{P.runnext有G?}
D -->|是| E[取出runnext的G<br/>继承时间片]
D -->|否| F{P.runq有G?}
F -->|是| G[从P.runq取出G]
F -->|否| H{全局队列有G?}
H -->|是| I[加锁取一批G<br/>放入P.runq<br/>取一个执行]
H -->|否| J{netpoller有就绪G?}
J -->|是| K[取出就绪的G]
J -->|否| L{其他P.runq有G?}
L -->|是| M[Work Stealing<br/>偷一半放入P.runq]
L -->|否| N[M进入休眠/idle]
C --> O[execute执行G]
E --> O
G --> O
I --> O
K --> O
M --> A
O --> P{G需要阻塞?}
P -->|channel阻塞| Q[G→_Gwaiting<br/>放入channel.recvq<br/>M继续调度]
P -->|网络I/O| R[G→_Gwaiting<br/>放入netpoller<br/>M继续调度]
P -->|syscall| S[G→_Gsyscall<br/>M进入syscall阻塞]
P -->|执行完毕| T[G→_Gdead<br/>回收]
P -->|继续执行| O
Q --> A
R --> A
T --> A
S --> U{syscall>10ms?}
U -->|是| V[sysmon强制handoff<br/>P解绑交给其他M]
U -->|否| W[syscall返回<br/>快速获取原P]
V --> X[syscall返回<br/>尝试获取空闲P<br/>或G入全局队列]
W --> O
X --> Y{获取P成功?}
Y -->|是| O
Y -->|否| Z[G入全局队列<br/>M进入idle]
Z --> A
调度优先级顺序(基于源码 findRunnable):
每 61 个 schedtick 检查全局队列(确保公平性,防止饥饿)
P.runnext(优先插队位,继承时间片,最高优先)
P.runq 本地队列(无锁,FIFO)
sched.runq 全局队列批量获取(有锁,取 len(runq)/2 个)
netpoller(网络 I/O 就绪的 G)
Work Stealing(从其他 P 偷一半)
找不到 → M 休眠
Channel 阻塞时序图
展示 channel 接收阻塞和发送唤醒的完整过程。
sequenceDiagram
participant G1栈 as G1栈(用户代码)
participant g0栈 as g0栈(runtime代码)
participant M as M(执行引擎)
participant ch as channel.recvq
participant G2栈 as G2栈(用户代码)
Note over M: M.curg = G1
rect rgb(230, 240, 255)
Note right of M: 在G1栈上执行
M->>G1栈: 执行用户代码
G1栈->>G1栈: x := <-ch
end
rect rgb(255, 245, 230)
Note right of M: 切到g0栈执行runtime
M->>g0栈: 切换栈
g0栈->>g0栈: chanrecv()
g0栈->>ch: enqueue(sudog)
g0栈->>g0栈: gopark()
Note over M: G1.status = _Gwaiting
end
rect rgb(240, 255, 240)
Note right of M: 调度循环
g0栈->>g0栈: schedule()
g0栈->>g0栈: 从P.runq取G2
M->>M: M.curg = G2
M->>G2栈: execute(G2)
end
Note over M: G1在recvq等待...
rect rgb(230, 240, 255)
Note right of M: 在G2栈上执行
M->>G2栈: 执行用户代码
G2栈->>G2栈: ch <- data
end
rect rgb(255, 245, 230)
Note right of M: 切到g0栈
M->>g0栈: 切换栈
g0栈->>g0栈: chansend()
g0栈->>ch: dequeue(sudog=G1)
g0栈->>g0栈: goready(G1)
Note over M: G1.status = _Grunnable
g0栈->>g0栈: G1放入P.runq
end
rect rgb(230, 240, 255)
Note right of M: G2返回继续执行
M->>G2栈: 返回用户栈继续
Note over M: G2执行到调度点<br/>阻塞/完成/抢占
end
rect rgb(255, 245, 230)
Note right of M: 调度点触发schedule
M->>g0栈: 切到g0栈
g0栈->>g0栈: schedule()
g0栈->>g0栈: 从P.runnext取G1
M->>M: M.curg = G1
M->>G1栈: execute(G1)
end
Note over M: G1拿到数据继续
执行栈切换说明:
栈
代码类型
执行内容
用户栈
Go 用户代码
x := <-ch、业务逻辑
g0栈
runtime 代码
chanrecv、gopark、schedule
关键点:
M 是执行引擎,在 g0栈 和用户栈之间切换
Channel 操作触发栈切换:用户栈 → g0栈 → runtime 处理
M 和 P 不解绑,阻塞的是 G,M 继续调度
goready 只入队,不立即切换:G2 发送后继续执行,直到调度点才切换到 G1
enqueue 放入的是 sudog(G的代理结构),不是 G 本身
Syscall 阻塞时序图
展示 syscall 阻塞的 handoff 机制和恢复过程。
核心理解:syscall 时 G 跟着 M,不像 channel 那样脱离。sysmon 监控并可能触发 handoff。
sequenceDiagram
participant G栈 as G栈(用户代码)
participant g0栈 as g0栈(runtime代码)
participant M1 as M(原线程)
participant P as P
participant sysmon as sysmon(独立M)
participant M2 as M(新线程)
participant 全局队列 as sched.runq
Note over M1: M.curg = G
rect rgb(230, 240, 255)
Note right of M1: 在G栈执行
M1->>G栈: 执行用户代码
G栈->>G栈: syscall.Read()
end
rect rgb(255, 245, 230)
Note right of M1: 切到g0栈
M1->>g0栈: 切换栈
g0栈->>g0栈: entersyscall()
Note over M1: G.status = _Gsyscall
g0栈->>g0栈: 保存现场, P.syscalltick++
end
rect rgb(255, 230, 230)
Note right of M1: M进入OS阻塞
M1->>M1: OS线程阻塞在syscall
Note over P: P仍绑定M1,状态为_Prunning
end
rect rgb(245, 230, 255)
Note right of sysmon: sysmon监控(不绑定P)
Note over sysmon: delay: 20μs→10ms
loop 监控循环
sysmon->>sysmon: retake()
sysmon->>P: 检查syscalltick+时间
alt syscall > 10ms 且有其他工作
sysmon->>P: handoffp()
Note over P: P解绑M1
sysmon->>M2: 找空闲M或创建新M
M2->>P: 绑定P
Note over P: P.runq中的G可执行
else 短时间 或 无工作
Note over sysmon: 不介入
end
end
end
rect rgb(255, 230, 230)
Note right of M1: syscall返回
M1->>M1: OS线程恢复
Note over M1: G仍跟着M1
end
rect rgb(255, 245, 230)
Note right of M1: 切到g0栈
M1->>g0栈: 切换栈
g0栈->>g0栈: exitsyscall()
g0栈->>P: 尝试快速获取原P
end
alt 获取原P成功
rect rgb(240, 255, 240)
Note right of M1: 快速路径
M1->>P: 重新绑定原P
g0栈->>G栈: 返回G栈继续
Note over M1: G继续执行
end
else 原P被handoff
rect rgb(255, 240, 230)
Note right of M1: 慢路径
g0栈->>g0栈: 尝试获取空闲P
alt 找到空闲P
M1->>P: 绑定空闲P
g0栈->>G栈: 返回G栈继续
else 无空闲P
g0栈->>全局队列: G放入全局队列
Note over M1: M进入idle
g0栈->>g0栈: stopm()
end
end
end
执行阶段说明:
阶段
颜色
执行位置
说明
用户代码
蓝色
G栈
syscall.Read()
runtime 处理
橙色
g0栈
entersyscall/exitsyscall
OS 阻塞
红色
M(OS线程)
内态态阻塞
sysmon 监控
紫色
sysmon.g0栈
独立M,不绑定P
快速恢复
绿色
原绑定
syscall返回快速获P
慢路径
黄色
调度
获取失败入全局队列
关键点:
syscall 阻塞时,G 跟着 M(不像 channel 那样脱离)
sysmon 监控,超过 10ms 且有其他工作时强制 handoff
P 解绑后交给其他 M 或进入 _Pidle
syscall 返回后 M 自己尝试获取 P(sysmon 不参与)
获取失败则 G 入全局队列
注意:P 没有 _Psyscall 状态。syscall 时 P 仍为 _Prunning。sysmon 通过检查 M.curg.status 判断这个 M 是否在执行 syscall。
Work Stealing 流程图
展示 P 本地队列空时的偷任务过程。
flowchart TD
A[P.runq 空] --> B[选择目标P<br/>随机或轮询]
B --> C{目标P.runq有G?}
C -->|否| D[选择下一个P]
D --> C
C -->|是| E[加锁目标P.runq]
E --> F[计算偷取数量<br/>n = runqsize/2]
F --> G[从尾部偷取n个G]
G --> H[放入自己的P.runq]
H --> I[解锁目标P.runq]
I --> J[取出一个G执行]
J --> K{执行成功?}
K -->|是| L[完成]
K -->|否| A
偷一半的设计原因:
原因
说明
减少偷取频率
偷一批而非单个,减少后续偷取次数
平衡负载
均衡各 P 的工作量
避免竞争
每个偷取操作需要加锁,减少加锁次数
整体架构图
展示 GMP 模型的整体架构。
flowchart TB
subgraph Global["全局共享数据 schedt"]
GRQ["sched.runq<br/>全局G队列(有锁)"]
PList["pidle<br/>空闲P列表"]
MList["midle<br/>空闲M列表"]
end
subgraph P1["P0"]
LRQ1["P.runq<br/>本地队列(256,无锁)"]
MC1["mcache<br/>内存缓存"]
end
subgraph P2["P1"]
LRQ2["P.runq"]
MC2["mcache"]
end
subgraph P3["P2"]
LRQ3["P.runq"]
MC3["mcache"]
end
subgraph M1["M0"]
G0_1["g0<br/>调度栈"]
CurG1["curg<br/>当前G"]
end
subgraph M2["M1"]
G0_2["g0"]
CurG2["curg"]
end
subgraph M3["M2"]
G0_3["g0"]
CurG3["curg"]
end
subgraph Special["特殊线程"]
SYS["sysmon<br/>系统监控(不绑定P)"]
end
Global <--> P1
Global <--> P2
Global <--> P3
P1 <--> M1
P2 <--> M2
P3 <--> M3
M1 --> LRQ1
M2 --> LRQ2
M3 --> LRQ3
LRQ1 <-.-> LRQ2
LRQ2 <-.-> LRQ3
SYS -.-> P1
SYS -.-> P2
SYS -.-> P3
var b []byteb = append(b, "bar"...) // append string contents b is []byte{'b', 'a', 'r' }
超出容量会自动扩容,即使用新的底层数组。
copy
copy(dst, src []T) intcopy(dst []byte, src string) int
这里返回的是赋值的元素个数,为min(len(dst), len(src))
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}var s = make([]int, 6)var b = make([]byte, 5)n1 := copy(s, a[0:]) // n1 == 6, s is []int{0, 1, 2, 3, 4, 5}n2 := copy(s, s[2:]) // n2 == 4, s is []int{2, 3, 4, 5, 4, 5}n3 := copy(b, "Hello, World!") // n3 == 5, b is []byte("Hello")
clear
清空map和slice
var m = make(map[string]string, 16)
clear(m) // delete all entries
len(m) // 0
如果值是nil,clear并不会报错,相当于什么都没有做
len and cap
Call Argument type Result
len(s) string type string length in bytes
[n]T, *[n]T array length (== n)
[]T slice length
map[K]T map length (number of defined keys)
chan T number of elements queued in channel buffer
type parameter see below
cap(s) [n]T, *[n]T array length (== n)
[]T slice capacity
chan T channel buffer capacity
type parameter see below
类型参数的类型集都必须支持len和cap。
0 <= len(s) <= cap(s)永远成立, 0 对应empty或nil
对于字符串(len)和数组,这个结果是个常量,编译期就被替换了。
make
针对slice,map,channel,返回对应类型的值,且进行了初始化。
Call Type T Result
make(T, n) slice slice of type T with length n and capacity n
make(T, n, m) slice slice of type T with length n and capacity m
make(T) map map of type T
make(T, n) map map of type T with initial space for approximately n elements
make(T) channel unbuffered channel of type T
make(T, n) channel buffered channel of type T, buffer size n
make(T, n) type parameter see below
make(T, n, m) type parameter see below
var x, y intm := min(x) // m == xm := min(x, y) // m is the smaller of x and ym := max(x, y, 10) // m is the larger of x and y but at least 10c := max(1, 2.0, 10) // c == 10.0 (floating-point kind)f := max(0, float32(x)) // type of f is float32var s []string_ = min(s...) // invalid: slice arguments are not permittedt := max("", "foo", "bar") // t == "foo" (string kind) 直接按物理字节来比较
特殊的浮点数值:
x y min(x, y) max(x, y)
-0.0 0.0 -0.0 0.0 // negative zero is smaller than (non-negative) zero
-Inf y -Inf y // negative infinity is smaller than any other number
+Inf y y +Inf // positive infinity is larger than any other number
NaN y NaN NaN // if any argument is a NaN, the result is a NaN
type Error interface { error // RuntimeError is a no-op function but // serves to distinguish types that are runtime // errors from ordinary errors: a type is a // runtime error if it has a RuntimeError method. RuntimeError()}
package unsafetype ArbitraryType int // shorthand for an arbitrary Go type; it is not a real typetype Pointer *ArbitraryTypefunc Alignof(variable ArbitraryType) uintptrfunc Offsetof(selector ArbitraryType) uintptrfunc Sizeof(variable ArbitraryType) uintptrtype IntegerType int // shorthand for an integer type; it is not a real typefunc Add(ptr Pointer, len IntegerType) Pointerfunc Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryTypefunc SliceData(slice []ArbitraryType) *ArbitraryTypefunc String(ptr *byte, len IntegerType) stringfunc StringData(str string) *byte
它定义了一个类型ArbitraryType,表示任意类型,它的底层类型int,没有意义,仅仅是个占位类型: it is not a real type,你也无法使用var i unsafe.ArbitraryType这种声明;同样的IntegerType表示任意整数类型。
s := "Hello"// 获取字符串数据的指针(只读!)ptr := unsafe.StringData(s)// 读取第一个字符fmt.Println(*ptr) // 72 ('H')// 遍历所有字符for i := 0; i < len(s); i++ { b := *(*byte)(unsafe.Add(unsafe.Pointer(ptr), uintptr(i))) fmt.Printf("%c ", b) // H e l l o}