1.sync.Once看不出来怎么实现的懒加载呀
- 调用 Do 的时候就已经执行用户函数了
- 除非你是在第一次使用资源的时候才 Do
2.当p的数量发生变化时,已有的poolLocal会跟着变化么?
- 会
3.为什么pool的shared通过双端链表能实现无锁
- 队列头队列尾 cas
4.为什么semaRoot是个treap而不是一个链表或者队列?这个地址是semaphore的地址?每个锁上不是一个独立的semephore么?
- 用链表查询是线性复杂度 O(n)
- 用数组可以用二分,但是增删麻烦
- 这个地址就是 sema 的地址:

5.不是按等待时间来唤醒竞争的goroutine么?
- 非饥饿模式:最新进自旋的 goroutine 优先级最高
- 饥饿模式:排队
6.如果sync.Mutex锁已经由一个协程持有,然后另外一个goroutine在加锁时,会有自旋过程吗?
- 会
7.面试的时候,如果面试官让写单例模式,需要写哪几种?需要和java中保持一致吗?
- double check, if atomic.Load == xxxx {} else {lock if }
8.sync.Map 设计 read 和 dirty 的目的是什么?为什么分开设计?
- map + lock 多核扩展性差一点
- sync.Map 在读多写少的情况下,基本上不需要加锁
9.如果多个g竞争锁,抢不到的g全挂起了,之后怎么产生的唤起抢锁行为呢?这个知识怎么跟GMP关联起来呢?
1 | runtime_SemacquireMutex -> sync_runtime_SemacquireMutex -> gopark -> schedule() 调度其它的 goroutine |
10.mutex加锁g阻塞打包成sudog挂在阻塞链表吗?当解锁操作时是怎么对链表操作的
- root.dequeue
11.sema,信号量,就是一个sudog的链表?所以RWMutex需要维护两个sema字段,分别保存读写的sudog?有点没清楚semtable、semaRoot、sema的关系?
- sema : sudog 的链表,sem addr 维护在 sematable 里,semtable = 251 treap
- 是的
12.atomic相关的操作,底层都会有产生自旋的可能吗? atomic相关的行为都是乐观锁的模式吗?
- atomic 底层在 x86/64 平台没自旋,但在 arm 平台可能会有
- for atomic 无锁算法, cas
13.接问题2,如果会变化的话,是有代码在监控p的数量么?
- 问答区见,我翻一下代码~
14.sync.Pool 去其他P的shared取任务会加锁吗?是只取一个放到当前P的局部吗?
- 不加锁
- 问答区见,需要翻一下代码~
15.sync.Map 性能
- 读多写少,sync.Map 好
16.sync.Map在Delete之后的那段行为没看懂,Delete之前dirty不是置空了么,为什么Store z的时候,会把y的值也从read里面带过来了呢?
- 这里有一个循环拷贝,会把 read 里所有还活着的拷贝到 dirty
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
m.read.Store(readOnly{m: read.m, amended: true})
}
func (m *Map) dirtyLocked() {
if m.dirty != nil {
return
}
read, _ := m.read.Load().(readOnly)
m.dirty = make(map[interface{}]*entry, len(read.m))
for k, e := range read.m {
if !e.tryExpungeLocked() {
m.dirty[k] = e
}
}
17.突然想到一个问题: 某个go协程运行了一半,被挂起了,恢复之后会重新运行一次代码吗?怎么记录上一次运行到哪里呢,,这个不是很懂
- 有 pc 寄存器保存之前运行到的代码位置的下一条指令位置
18.sudog 与 普通G 是两套调度方案吗? sudog 不会插入到本地或者全局Q中吗? 如果也是插入本地或者全局Q,那么唤醒的时候不就不遵从GMP规则里面的调度了吗?
- 第一课,sudog
g
19.原子操作是怎么实现的锁呢?假如几个核心同时去写呢?mesi又是怎么选择哪个去写的呢?
1 | for { |
- 饥饿模式
- mesi 协议相当于是 CPU 内部的同步机制,核心执行啥代码肯定知道的
20.并发保序的 可以发下源码位置吗
21.supervisord 监控的实现可以展开讲讲吗
- 代码 panic 未捕获、fatal 之类意外退出,自动拉起进程,防止 api server down
- stderr/stdout 日志重定向
- 崩溃日志在 stderr
22.闭包的那个,为什么i := i 可以解决问题
- https://stackoverflow.com/questions/40326723/go-vet-range-variable-captured-by-func-literal-when-using-go-routine-inside-of-f
- loop variable capture

24.线上应用崩,如何 dump groutine 栈,曹大好像有开源相关项目?
- github.com/mosn/holmes
- dump goroutine 文本栈成本还是挺高,慎用
- 设阈值要慎重,最好是进程已经没法服务的时候 dump 一下
25.函数返回结构体的时候,如果小于64字节,通过值传递是不是就可以直接使用cpu l1 cache
- Go 现在是放栈上的
- 以后是在寄存器里
- 栈和寄存器都是软件可以使用的工具,cacheline 没法在应用层使用的,对用户是透明的
26.如果产生了false sharing行为,这个错误的数据是只有短暂时间会产生么? 会“等一段时间”后CPU或者系统修复这种数据么?还是永远错误,需要显式reload
- false sharing 只会导致性能问题,不会导致逻辑错误
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 lihuanjie113@gmail.com