【Go学习】语言类库(一)unsafe和context

108 阅读2分钟

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 image.png

  • 不要将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"
}

image.png

  • 空context不会被取消,没有值,也没有ddl

空context的使用形式如下: image.png

关于cancelCtx

image.png

函数返回的是一个只读的channel,直接读这个channel的协程会被阻塞。一般搭配select来使用。

cancel()函数:

image.png 运行机制:将c.done关闭,递归取消所有子节点,最后从父节点中删除自己。 这种机制也可以用于让协程接收到取消信号。就是通过关闭channel的方式,将信号传递给所有子节点。

参考资料

  • Go源码 -《Go程序员面试笔试宝典》