context
上下文信息传递,还提供了超时(Timeout)和取消(Cancel)的机制
1个接口
context.Context是个接口,定义了4个方法
- Deadline:返回 被取消的截止日期
- Done:返回一个 Channel 对象。在 Context 被取消时,此 Channel 会被 close
- Err:返回 Done 被 close 的原因
- Value:返回此 ctx 中和指定的 key 相关联的 value
4个具体实现
- emptyCtx
- 本质是个整形(int),对接口的实现只是简单的返回nil,false
- chanelCtx
- 可取消的context
- timerCtx
- 在cancelCtx基础上,封装了一个定时器和一个截止时间
- valCtx
- 给context附加一个键值对信息
6个函数
- Background
- 返回一个非 nil 的、空的 Context
- 一般用在主函数、初始化、测试以及创建根Context 的时候
- 类型是 emptyCtx
- TODO
- 返回一个非 nil 的、空的 Context
- 当你不清楚是否该用 Context,或者目前还不知道要传递一些什么上下文信息的时候,就可以使用这个方法。
- 类型是 emptyCtx
- WithCancel
- 返回 parent 的副本,只是副本中的 Done Channel 是新建的对象
- 类型是 cancelCtx。
- WithDeadline
- 返回 parent 的副本,并且设置了一个截止时间
- 类型是 timerCtx。
- WithTimeout
- 和 WithDeadline 一样,只不过设置的是超时时间
- 类型为是timerCtx
- WithValue
- 基于 parent Context 生成一个新的 Context,保存了一个 key-value 键值对
- 类型是 valueCtx
Atomic原子操作
在其它线程看来,原子操作要么执行完了,要么还没有执行
方法
atomic 操作的对象是一个地址
- Add:给地址中的值增加某个值
- CAS:比较地址内的值,相同才替换
- Swap:替换地址中的值,返回旧值
- Load:取出地址中的值,原子操作
- Store:往地址存值,原子操作
- Value 类型:原子地存取对象类型,但也只能存取,不能 CAS 和 Swap
使用 atomic 和直接内存操作的区别
- atomic 提供内存屏障的功能,保证赋值的数据完整性、可见性,一旦一个核更新了该地址的值,其它处理器总是能读取到它的最新值
- 内存,撕裂写(两个指令,执行一个,其他人看到更新一半的数据)
Channel
通过通信共享内存,而不是通过共享内存而实现通信
基本用法
- 发送数据
- 接收数据
- 关闭chan
结构体
- qcount:循环队列元素的数量
- dataqsiz:循环队列的大小。
- buf:循环队列的指针。
- elemtype 和 elemsize:chan 中元素的类型和 size。
- sendx:处理发送数据的指针在 buf 中的位置。
- recvx:处理接收请求时的指针在 buf 中的位置。
- recvq:接收者等待队列。
- sendq:发送者等待队列。
- lock:互斥锁
常见错误
- panic
- close 为 nil 的 chan;
- send 已经 close 的 chan;
- close 已经 close 的 chan。
- 内存泄露
- 缺少接收端或者发送端,或者提前结束接收端或者发送端没有close channel
应用场景
- 消息交流:生产者-消费者
- 消息传递:Goroutine数据交换
- 信号通知:Goroutine信号传递
- 锁
- 任务编排:让goroutine按照顺序并发
- Or-Done模式:
- 信号通知模式
- 多个任务,只要一个任务执行完,就获得信号
- 扇入模式:多个输入源 Channel 、一个目的 Channel 输出
- 扇出模式:一个输入源 Channel,多个目的 Channel输出
- Stream:把 Channel 当作流式管道使用的方式
- Map-Reduce:一种处理数据的方式
- Or-Done模式:
Semaphore(信号量)
一个变量加一些并发控制的能力(最简单的信号量)
实现
- 初始化信号量:设定初始的资源的数量
- P 操作:将信号量的计数值减去 1
- V 操作:将信号量的计数值加 1
类型
- 二进位信号量:互斥的功能(要么是 0,要么是 1)
- 计数信号量:限制多个goroutine同时访问某个资源的数量
Go 官方扩展库信号量的实现semaphore.Weighted
方法
- Acquire:相当于 P 操作。先入先出,第一个等待者获取够资源,才会到下一个
- Release:相当于 V 操作
- TryAcquire:尝试获取 n 个资源,但是它不会阻塞,要么成功获取 n 个资源,返回 true,要么一个也不获取,返回 false。
结构体
- size int64 // 最大资源数
- cur int64 // 当前已被使用的资源
- mu sync.Mutex // 互斥锁,对字段的保护
- waiters list.List // 等待队列
常见错误
- 请求了资源,但是忘记释放它
- 释放了从未请求的资源
使用错误
- Release 时,传递一个比请求到的数量大的数值, panic。
- Release 时,传递一个负数,会导致资源永久被持有。
- Acquire,请求的资源数比最大的资源数大,可能永远被阻塞(依赖ctx的状态返回,否则一直等待)。
SingleFlight
作用
- 在处理多个goroutine 同时调用同一个函数的时候,只让一个 goroutine 去调用这个函数,等到这个goroutine 返回结果的时候,再把结果返回给这几个同时调用的 goroutine,这样可以减少并发调用的数量。
- 并发请求合并成一个请求
结构体
- mu sync.Mutex // protects m
- m map[string]*call // lazily initialized
方法
- Do
- 执行一个函数,并返回函数执行的结果
- 提供一个 key,对于同一个 key,在同一时间只有一个在执行
- DoChan
- 类似 Do 方法,只不过是返回一个 chan,等 fn 函数执行完,就能从 chan 中接收返回结果
- Forget
- 忘记这个 key。之后这个 key 请求会执行 f,而不是等待前一个未完成的 fn 函数的结果。
循环栅栏 CyclicBarrier
作用
- 一组 goroutine 彼此等待,到达一个共同的执行点(都在栅栏前等待,全部到齐,抬起栅栏放行)。
- 可以被重复使用,所以叫循环栅栏
初始化方法
- New :指定循环栅栏参与者的数量
- NewWithAction:额外提供一个函数,可以在每一次到达执行点的时候执行一次
CyclicBarrier接口
- Await(ctx context.Context) error // 等待所有参与者到达,被ctx.Done()中断,返回ErrBrokenBarrier
- Reset() // 重置到初始化状态。如果当前有等待者,那么它们会返回ErrBrokenBarrier
- GetNumberWaiting() int // 返回当前等待者的数量
- GetParties() int // 参与者的数量
- IsBroken() bool // 循环栅栏是否处于中断状态