go basics
go-basics
string
fields&maps
func WordCount(s string) map[string]int {
cntMap := make(map[string]int)
strs := strings.Fields(s)
for _, s := range strs {
i := cntMap[s]
cntMap[s] = i + 1
}
return cntMap
}
- string按空格拆分成列表
strs := strings.Fields(s)
- 遍历list
for _, s := range strs {
// xxx
}
- 注意: 自加不能赋值(与Java有区别)
// 如下是非法的.
y := x++
//
x++
y = x
- append
map
package core
import "log"
type NUMANodeMap map[int]string
func iter() {
nodeMap := NUMANodeMap{
1: "aa",
2: "bb",
}
// 遍历key
for n := range nodeMap {
// n 代表key
log.Println(n)
// nodeMap[n] 获取value
log.Println(nodeMap[n])
}
for k, v := range nodeMap {
// k 代表key
// v 代表value
log.Println(k, v)
}
// map中增加kv
nodeMap[3] = "cc"
// map中移除kv
delete(nodeMap, 1)
log.Println(nodeMap)
}
struct
参见: Create, initialize and compare structs · YourBasic Go
简单struct初始化
func TestVertex_Init(t *testing.T) {
// 单行初始化, 末尾不用 逗号
v := Vertex{23.2, 22.3}
log.Println(v)
// 多行初始化, 末尾需要 逗号
vertex := Vertex{
23.2,
22.3,
}
log.Println(vertex)
// 部分初始化, 需要指定 name, 末尾需要 逗号
vertex2 := Vertex{
X: 1.2,
}
log.Println(vertex2)
var vertexP *Vertex
vertexP = new(Vertex)
vertexP.X = 1.2
log.Println(vertexP)
log.Println(*vertexP)
assert.Equal(t, *vertexP, vertex2)
assert.Equal(t, vertexP, &vertex2)
assert.False(t, vertexP == &vertex2)
vertexP2 := &Vertex{
1.2,
2.2,
}
log.Println(vertexP2)
vertexP3 := &Vertex{
Y: 1.2,
}
log.Println(vertexP3)
}
复杂struct初始化
需要指定内部的struct类型(如下例子中的 Vertext )
func TestTriangle_Init(t *testing.T) {
v1 := Vertex{1.2, 2.2}
triangle := Triangle{
v1,
Vertex{2, 3},
Vertex{3, 4},
}
log.Println(triangle)
}
struct embedding
如下定义的结构代表啥意思? 为啥第一个 *scheduleroptions.Options 没有filedName呢?
// Options has all the params needed to run a Scheduler
type Options struct {
*scheduleroptions.Options
CombinedInsecureServing *CombinedInsecureServingOptions
}
实际上使用了: Go by Example: Struct Embedding 方式, 即类似Java的继承
pointer
方法定义中, Pointer Receiver 与 普通Receiver, 如下 Scale 与 Scale2, 实际上Scale2是没有生效的, 是没有修改原来的值的.
所以
- PointerReceiver: 类似Java类中的普通方法
- 普通Receiver: 类似Java类中的Static方法
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v Vertex) Scale2(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
goroutine
如何获取到goroutine的ID?
参见: https://stackoverflow.com/questions/75361134/how-can-i-get-a-goroutines-runtime-id
故意不让我们获取到.
注意: 一个goroutine crash, 会导致整个go进程挂掉. 因此需要保护,
- 保护方式参见: go - How do you keep goroutines running if another one crashes? - Stack Overflow
- 为啥如此设计: Why does Go terminate the whole process if one goroutine paincs? - Technical Discussion - Go Forum 个人理解类似Java的RTE/UncatchedException, 如果不catch, 则会导致进程Crash. 但区别是, Java中通常需要进行catch, Go中有类似的方法recover(), 但Go中不推荐.
go func()
go func() 新建的协程竟然可以直接引用局部变量(而不是通过传参方式). 所以是不是很容易把线程不安全的对象泄露?
func localVar() {
var myVar = "hello"
//方式1: 临时创建 匿名方法的 routine
go func() {
myVar = "Nice"
}()
time.Sleep(1 * time.Second)
log.Println(myVar)
//方式2: 直接在方法调用前增加 go 关键词
go localVar2()
}
func localVar2() {
log.Println("localVar2")
}
go func/chan sample
在k8s代码中, 看到如下代码, 代表啥意思?
func SetupSignalHandler() <-chan struct{} {
return SetupSignalContext().Done()
}
因此通常的使用方式是:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
stopCh := server.SetupSignalHandler()
<-stopCh
cancel()
}()
routine tree
context
How To Use Contexts in Go | DigitalOcean
channel
var msgChan = make(chan string)
func sayHello() {
log.Println("sayHello started")
go func() {
msgChan <- "ping"
log.Println("ping sent")
msgChan <- "pong"
log.Println("pong sent")
}()
time.Sleep(3 * time.Second)
msg := <-msgChan
log.Println(msg)
msg = <-msgChan
log.Println(msg)
log.Println("done")
}
普通channel
var msgChan = make(chan string)
普通channel: 只有在收到receive的时候(即receive注册之后), 才会真正send出去. 否则send会一直阻塞.
By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value.
即类似Java中的synchronirousQueue, 必须消费者注册/开始消费的时候, 生产者才能执行.
2023/12/05 17:41:29 sayHello started
2023/12/05 17:41:32 ping sent
2023/12/05 17:41:32 ping
2023/12/05 17:41:32 pong sent
2023/12/05 17:41:32 pong
2023/12/05 17:41:32 done
buffered channel
var msgChan = make(chan string, 2)
- 运行结果如下, 生产者可以直接放进去, 而不用等消费者注册.
2023/12/05 17:39:34 sayHello started
2023/12/05 17:39:34 ping sent
2023/12/05 17:39:34 pong sent
2023/12/05 17:39:37 ping
2023/12/05 17:39:37 pong
2023/12/05 17:39:37 done
- 如果生产者放入的size大于buffer, 则多余部分会一直卡住. 直到buffer中被消费了1个, 则可以放入1个.
channel方向
作为方法参数时, 可以指定channel方向, 这样防止
只写: chan<-
只读: <-chan
// 只写的channel
// 如果改成 (done <-chan bool) 则会编译错误.
func worker(done chan<- bool) {
log.Println("working...")
time.Sleep(time.Second)
log.Println("done...")
done <- true
}
panic&recover
如何手动触发panic: panic("test")
mod
go get/go install
go mod download
git上checkout下来的项目, 可以直接使用该命令, 把依赖包都下载下来.
本地如果有多个go mod工程, 如何进行相互依赖?
- 目前看只能使用
replace的方式. 感觉不太方便. - 否则就只能代码push到地址中, 使用
go mod download进行下载. 太不方便. - 为啥不能像maven一样, 可以先安装到本地. 然后其他模块依赖本地的即可.
other
方法名是包级别的. 即一个包下所有的.go文件, 方法不能同名. 而java中是类级别(文件级别)
goland不支持go module, 导致依赖无法下载/跳转, 需要手动enable下:

