Buildin
buildin 文档 https://golang.org/pkg/builtin
uintptr
足够大、可以存放任何指针地址的类型。常用来将指针转换后进行寻址计算。
uintptr is an integer type that is large enough to hold the bit pattern of any pointer.
gofunc Offsetof(x ArbitraryType) uintptr func Sizeof(x ArbitraryType) uintptr func Alignof(x ArbitraryType) uintptr
错误示例
func main(){
n := Num{i: "EDDYCJY", j: 1}
nPointer := unsafe.Pointer(&n)
...
ptr := uintptr(nPointer)
njPointer := (*int64)(unsafe.Pointer(ptr + unsafe.Offsetof(n.j)))
...
}
uintptr 类型不能存储在临时变量中。因为从 GC 的角度来看,uintptr 类型的临时变量只是一个无符号整数,并不知道它是一个指针地址。因此当满足一定条件后,ptr这个临时变量是可能被垃圾回收掉的。
ArbitraryType
表示任意类型,实际上是一个占位符
ArbitraryType is here for the purposes of documentation only and is not actually part of the unsafe package. It represents the type of an arbitrary Go expression.
gotype ArbitraryType int
Fmt
Printf 标准输出
| 动词 | 含义 |
|---|---|
| %v | 按值的本来值输出 |
| %+v | 在 %v 基础上,对结构体字段名和值进行展开 |
| %#v | 输出 Go 语言语法格式的值 |
| %T | 输出 Go 语言语法格式的类型和值 |
| %% | 输出 % 本体 |
| %b | 整型以二进制方式显示 |
| %o | 整型以八进制方式显示 |
| %d | 整型以十进制方式显示 |
| %x | 整型以十六进制方式显示 |
| %X | 整型以十六进制、字母大写方式显示 |
| %U | Unicode 字符 |
| %f | 浮点数 |
| %p | 指针,十六进制方式显示 |
参考
- https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter01/01.3.html
- https://yourbasic.org/golang/fmt-printf-reference-cheat-sheet/
Flag
func main() {
flag.BoolVar(&all, "all", false, "Set to everything")
flag.Parse()
fmt.Println(all)
}
// true
设置过的 flag 参数记得要 parse 。
Reflect
获取一个结构体中某个字段的 tag 的信息
func GetStructTagList(i interface{}, tag string) []string {
v := reflect.ValueOf(i)
t := reflect.TypeOf(i)
count := v.NumField()
var ret []string
for i := 0; i < count; i++ {
ret = append(ret, fmt.Sprintf("%v", t.Field(i).Tag.Get(tag)))
}
return ret
}
ValueOf
TypeOf
NumField
Signal
监听一个信号:
func main() {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT)
fmt.Println("start...")
go func() {
switch sig := <-sigs; sig.String() {
case syscall.SIGINT.String():
done <- true
default:
fmt.Println("unknown signal")
}
}()
for {
<-done
fmt.Println("done")
break
}
}
Context
WithTimeout
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
work(ctx)
}
func work(ctx context.Context) {
done := make(chan bool)
go func(ctx context.Context) {
println("工作中...")
time.Sleep(time.Second * 3)
done <- true
}(ctx)
select {
case <-ctx.Done():
fmt.Println("超时了", ctx.Err())
case <-done:
fmt.Println("执行完成")
}
}
Sync
Waitgroup
-
Add
-
Done
-
Wait
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 每启动一个 goroutine 增加计数
go func(id int) {
defer wg.Done() // 确保在任何退出路径都会减少计数
time.Sleep(time.Second * time.Duration(id))
fmt.Printf("Worker %d completed\n", id)
}(i)
}
fmt.Println("Waiting for workers...")
wg.Wait() // 阻塞直到所有 goroutine 完成
fmt.Println("All workers completed")
}
Mutex 互斥锁
-
Lock
-
Unlock
func main() {
var mutex sync.Mutex
count := 0
for i := 0; i < 50; i++ {
go func() {
mutex.Lock()
count++
mutex.Unlock()
}()
}
time.Sleep(time.Second)
fmt.Println(count)
}
// 50
RWMutex 读写锁
A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. The zero value for a RWMutex is an unlocked mutex.
A RWMutex must not be copied after first use.
- Lock 写锁,对读写锁读或写都将阻塞。
- Unlock
- RLock 读锁,只对写操作阻塞。
- RUnlock
RLock() 为“读锁定”,调用“读锁定”后,不能有其他 goroutine 进行写操作,但是可以进行读操作。RUnlock() 为“读解锁”,调用“读解锁”后,会唤醒一个因为要进行“写锁定”而被阻塞的 goroutine。
当 mutex 在 struct 中时,则整个 struct 拥有互斥锁。
Once
var once sync.Once
once.Do(func() {
fmt.Println("init config ...")
})
Atomic
func AddT(addr *T, delta T)(new T)
func LoadT(addr *T) (val T)
func StoreT(addr *T, val T)
func SwapT(addr *T, new T) (old T)
func CompareAndSwapT(addr *T, old, new T) (swapped bool)
可以用 atomic 实现 CAS 乐观锁
Pool
func test() {
// 初始化一个pool
pool := &sync.Pool{
// 默认的返回值设置,不写这个参数,默认是nil
New: func() interface{} {
return 0
},
}
// 看一下初始的值,这里是返回0,如果不设置New函数,默认返回nil
fmt.Println(pool.Get())
fmt.Println(pool.Get())
// 设置一个参数1
pool.Put(1)
pool.Put(1)
// 获取查看结果
fmt.Println(pool.Get())
fmt.Println(pool.Get())
// 再次获取,会发现,已经是空的了,只能返回默认的值。
fmt.Println(pool.Get())
// 0
// 0
// 1
// 1
// 0
}
Cond
noCopy
noCopy may be embedded into structs which must not be copied after the first use.
https://github.com/golang/go/issues/8005#issuecomment-190753527
Go中没有原生的禁止拷贝的方式,所以如果有的结构体,你希望使用者无法拷贝,只能指针传递保证全局唯一的话,可以这么干,定义一个结构体叫 noCopy,要实现 sync.Locker 这个接口。
Map
Unsafe
Pointer
Alignof
Sizeof
Offsetof
ArbitraryType(int)
Runtime
GOMAXPROCS
gofunc GOMAXPROCS(n int) intGOMAXPROCS sets the maximum number of CPUs that can be executing simultaneously and returns the previous setting. If n < 1, it does not change the current setting. The number of logical CPUs on the local machine can be queried with NumCPU. This call will go away when the scheduler improves.
疑问
- 在本地设置了最大的 CPU 数量如何验证生效?
Gosched
runtime.Gosched()用于让出CPU时间片。这就像跑接力赛,A跑了一会碰到代码runtime.Gosched()就把接力棒交给B了,A歇着了,B继续跑。
func say(s string) {
for i := 0; i < 2; i++ {
fmt.Printf("say: %s\n", s)
runtime.Gosched()
fmt.Println(s)
}
}
func test() {
fmt.Println(runtime.GOMAXPROCS(0))
go say("world")
say("hello")
}
// 12
// say: hello
// say: world
// hello
// say: hello
// world
// say: world
// hello
因为 say("hello") 这句占用了时间,等它执行完,线程也结束了,say("world") 就没有机会了。
这里同时可以看出,go 中的 goroutines 并不是同时在运行。事实上,如果没有在代码中通过 runtime.GOMAXPROCS(n) 指定使用多核的话,goroutines 都是在一个线程里的,它们之间通过不停的让出时间片轮流运行,达到类似同时运行的效果。
Stack
gofunc Stack(buf []byte, all bool) intStack formats a stack trace of the calling goroutine into buf and returns the number of bytes written to buf. If all is true, Stack formats stack traces of all other goroutines into buf after the trace for the current goroutine.
IO
ioutil
Bufio
URL
Timer
After
Sleep
Tick
Duration
Time
格式化基准 2006-01-02 15:04:05