6.并发编程最佳实践

  1. 1.sync.Once看不出来怎么实现的懒加载呀
  2. 2.当p的数量发生变化时,已有的poolLocal会跟着变化么?
  3. 3.为什么pool的shared通过双端链表能实现无锁
  4. 4.为什么semaRoot是个treap而不是一个链表或者队列?这个地址是semaphore的地址?每个锁上不是一个独立的semephore么?
  5. 5.不是按等待时间来唤醒竞争的goroutine么?
  6. 6.如果sync.Mutex锁已经由一个协程持有,然后另外一个goroutine在加锁时,会有自旋过程吗?
  7. 7.面试的时候,如果面试官让写单例模式,需要写哪几种?需要和java中保持一致吗?
  8. 8.sync.Map 设计 read 和 dirty 的目的是什么?为什么分开设计?
  9. 9.如果多个g竞争锁,抢不到的g全挂起了,之后怎么产生的唤起抢锁行为呢?这个知识怎么跟GMP关联起来呢?
  10. 10.mutex加锁g阻塞打包成sudog挂在阻塞链表吗?当解锁操作时是怎么对链表操作的
  11. 11.sema,信号量,就是一个sudog的链表?所以RWMutex需要维护两个sema字段,分别保存读写的sudog?有点没清楚semtable、semaRoot、sema的关系?
  12. 12.atomic相关的操作,底层都会有产生自旋的可能吗? atomic相关的行为都是乐观锁的模式吗?
  13. 13.接问题2,如果会变化的话,是有代码在监控p的数量么?
  14. 14.sync.Pool 去其他P的shared取任务会加锁吗?是只取一个放到当前P的局部吗?
  15. 15.sync.Map 性能
  16. 16.sync.Map在Delete之后的那段行为没看懂,Delete之前dirty不是置空了么,为什么Store z的时候,会把y的值也从read里面带过来了呢?
  17. 17.突然想到一个问题: 某个go协程运行了一半,被挂起了,恢复之后会重新运行一次代码吗?怎么记录上一次运行到哪里呢,,这个不是很懂
  18. 18.sudog 与 普通G 是两套调度方案吗? sudog 不会插入到本地或者全局Q中吗? 如果也是插入本地或者全局Q,那么唤醒的时候不就不遵从GMP规则里面的调度了吗?
  19. 19.原子操作是怎么实现的锁呢?假如几个核心同时去写呢?mesi又是怎么选择哪个去写的呢?
  20. 20.并发保序的 可以发下源码位置吗
  21. 21.supervisord 监控的实现可以展开讲讲吗
  22. 22.闭包的那个,为什么i := i 可以解决问题
  23. 24.线上应用崩,如何 dump groutine 栈,曹大好像有开源相关项目?
  24. 25.函数返回结构体的时候,如果小于64字节,通过值传递是不是就可以直接使用cpu l1 cache
  25. 26.如果产生了false sharing行为,这个错误的数据是只有短暂时间会产生么?  会“等一段时间”后CPU或者系统修复这种数据么?还是永远错误,需要显式reload

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
2
3
runtime_SemacquireMutex -> sync_runtime_SemacquireMutex ->  gopark -> schedule() 调度其它的 goroutine

wake : semrelease -> readyWithTime -> ready -> runqput

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
    18
    if !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
2
3
4
5
6
for {
if atomic.Cas() == true {
return
}
pause()
}
  • 饥饿模式
  • mesi 协议相当于是 CPU 内部的同步机制,核心执行啥代码肯定知道的

20.并发保序的 可以发下源码位置吗

21.supervisord 监控的实现可以展开讲讲吗

  • 代码 panic 未捕获、fatal 之类意外退出,自动拉起进程,防止 api server down
  • stderr/stdout 日志重定向
  • 崩溃日志在 stderr

22.闭包的那个,为什么i := i 可以解决问题

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

×

喜欢就点赞,疼爱就打赏