20230120日志

20230120日志

2023年1月20日
2023
202301, 线程, goroutine, data race, race condition, 内存模型, context

golang #

基本概念 #

不能直接创建Thread, 但是可以创建goroutine; os线程由os调度, goroutine在os线程内由goruntime调度; GOMAXPROCS定义产生多少OS线程, 在有block时, 还会生成新的OS线程, GOMAXPROCS默认等于机器的逻辑核心数; goroutine的状态, Executing: 在OS线程上运行, Runnable: 等待进入Executing, Waitting: stopped并等待一些事情完成(system call, sync, wait mutex); 1.14之前只会在chan send/recieve, i/o, wait mutext时发生context switch, 之后的版本会将运行超过10ms的goroutine标记为preemptible, 并有机会context-switched off被其他goroutine替换

goroutine_arrange

使用mutex还是channel #

通常并行使用mutext, 并发使用channel

mutext_or_channel

Data race 和 Race conditions #

  • Data race: 不同的goroutine,同时访问相同的内存, 至少一个goroutine在write
  • Race conditions: 不是Data race的场景也可能是Race conditions; 不确定哪个goroutine先运行; 不确认channel发送数据多快; 不确定读取数据库的耗时

内存模型 #

内存模式是为了保证避免 Data race

  1. 无Data race
i := 0
go func() {
  i++
}()
  1. 有Data race
i := 0
go func() {
  i++
}()
fmt.Println(i)
  1. 无Data race
i := 0
ch := make(chan struct{})
go func() {
  <-ch
  fmt.Println(i)
}()
i++
ch <- struct{}{}

variable increment < channel send < channel receive < variable read

  1. 无Data race
i := 0
ch := make(chan struct{})
go func() {
  <-ch
  fmt.Println(i)
}()
i++
close(ch)

variable increment < close < channel receive < variable read

  1. 有Data race(有缓冲)
i := 0
ch := make(chan struct{}, 1) // 有缓冲
go func() {
  i = 1
  <-ch
}()
ch <- struct{}{}
fmt.Println(i)

with_buffer

  1. 无Data race(无缓冲)
i := 0
ch := make(chan struct{})
go func() {
  i = 1
  <-ch
}()
ch <- struct{}{}
fmt.Println(i)

no_buffer

启动多少线程 #

  1. cpu bound类型的使用和GOMAXPROCS一致的数据,不到GOMAXPROCS时和任务数量一致
  2. i/o bound类型需要根据外部系统判断

Context的理解 #

  1. 1个接口(Context)
  2. 4个具体实现(emptyCtx, cancelCtx, timerCtx, valueCtx)
  3. 6个方法(Background, TODO, WithCancel, WithDeadline, WithTimeout, WithValue)

context_interface context_cancel context_value