unsafe的妙用
unsafe不能乱用,但是明白它这样做也是可行的就好了,就当掌握一点高级技巧。
修改私有成员
- 原理:分配给结构体的内存是一块连续的地址,所以可以通过offset定位到某个成员的地址,进而修改它的值。
- 实现:私有成员变量不能取offset,但可以通过unsafe.Sizeof()获取成员大小 如:
lang:=(*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p))+unsafe.Sizeof(int(0)+unsafe.Sizeof(string(""))))
*lang="Golang"
获取slice和map的长度
- 原理:通过unsafe.Pointer()和uintptr进行转换
- 实现: slice:makeslice返回的是unsafe.Pointer这个结构体指针
var len=*(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s))+uintptr(8)))
map:makemap返回的是*hmap指针
count:=**(**int)(unsafe.Poniter(&mp))
实现字符串和byte切片的zero-copy
- 原理:利用指针强转共享底层的Data和Len
func string2bytes(s string)[]byte{
return *(*[]byte)(unsafe.Pointer(&s))
}
context详解
context是什么
context是一个操作系统的概念,中文译名"上下文"。在Go中上下文的服务对象指代的是协程。
context是并发安全的吗?
是。
context的作用
- 传递共享数据 如拼接请求处理的过程
- 定时取消
使用
context.WithTimeout()定时使得请求超时就取消,避免因为网络延迟、机器负载等因素造成的请求阻塞。 - 防止协程泄露
使用
context.WithCancel()取消协程。
context的使用注意事项
源码路径:$GOROOT/src/context/context.go
- 不要将context放到结构体中
- context应该作为函数的第一个参数
- 不要向函数传入一个含nil的context,如果没想好传什么,就先打个TODO
- 只将context用于请求范围内的数据,如传输进程和API的,不要用来向函数传递额外的参数。
context接口
通用ctx接口
源码中的context实际上给出了四个方法(这里把源码里面的注释都删掉了:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
emptyctx
也给了一个context的实现,这是一个空的context:
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key any) any {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
- 空context不会被取消,没有值,也没有ddl
空context的使用形式如下:
关于cancelCtx
函数返回的是一个只读的channel,直接读这个channel的协程会被阻塞。一般搭配select来使用。
cancel()函数:
运行机制:将c.done关闭,递归取消所有子节点,最后从父节点中删除自己。
这种机制也可以用于让协程接收到取消信号。就是通过关闭channel的方式,将信号传递给所有子节点。
参考资料
- Go源码 -《Go程序员面试笔试宝典》