• hd167
    2020-01-10
    “原子操作在进行的过程中是不允许中断的。在底层,这会由 CPU 提供芯片级别的支持,所以绝对有效。即使在拥有多 CPU 核心,或者多 CPU 的计算机系统中,原子操作的保证也是不可撼动的。”这句话不知道该怎么理解?是不是如果一个原子操作在进行中,这台计算机的其他cpu核心都不能进行相关操作?
    举个例子,有两个Int32的变量a,b,如果一个线程x要对a做原子加法操作,另一个线程y想要对b做原子交换操作,多核cpu的话,线程x,y应该可以在不同的物理核心上同时进行操作吧?
    但是如果只有一个Int32的变量c,线程x要对c做原子加法操作,线程y要对c做原子交换操作,多核cpu的
    话可,线程x和线程y还可以在不同的物理核心上同时进行吗?

    作者回复: 任何原子操作都是真对某一个共享资源而言的,即内存中的同一个小块区域以及相关的缓存。这个区域足够小,因此通常只用一个CPU指令就可以完成对它的读或写。

    所以,这并不是说,一遇到原子操作,所有的CPU核心就都不工作了。你要理解“中断”这个词的真正含义。这里的细节还是很多的,涉及到cache line、多核CPU协调机制(几种方案)、LOCK原语(CPU不同方案不同)等等。一句两句说不太清楚,你可以自己去找资料看看。

    总之,你可以理解为,针对不同共享资源的原子操作是有可能并行进行的。而针对相同共享资源的原子操作,即使存在多CPU核心,它们页不可能并行进行。

     1
     4
  • Richard
    2019-09-25
    "不过,在同一时刻,只可能有少数的 goroutine 真正地处于运行状态,并且这个数量只会与 M 的数量一致,而不会随着 G 的增多而增长。" 个人感觉同时运行的g应该是和p数量相等

    作者回复: 不一定,P 把 G 交给 M 就不管了,调度器发现还有 M 闲着就会继续从那些 P 那边拿 G。

    
     1
  • Richard
    2019-12-24
    “这个中断的时机有很多,任何两条语句执行的间隙,甚至在某条语句执行的过程中都是可以的。即使这些语句在临界区之内也是如此“ 我对这段话里的临界区内也能产生中断有些疑问,我查阅相关资料后理解为,CPU提供了一些原子操作机制,os或者语言api使用这些原子操作实现了锁,使用锁能保证更大范围的原子性,也就是说我理解的临界区也是一组不可中断的操作,也是具有原子性的;而且单核上进入临界区会关中断,离开临界区会开中断;所以郝大我这里是不是理解有误?

    作者回复: 临界区是指互斥锁保护的区域。锁只能保证操作上的互斥,或者说串行,但不能保证不被中断。“互斥”和“不中断”是两个不同的概念。锁保护的代码没有原子性一说,在执行的过程中是有可能被中断的,即可能会被撤下CPU,转而运行其他并发的代码。

    互斥锁里面会用到原子操作,但那只是其中的一个或几个步骤而已。原子操作只能针对原始的内存地址来做,其中的一个原因是它对CPU的性能影响巨大。也正因为如此,原子操作根本无法顾及某段代码这么大颗粒度的东西。

    
    
  • benying
    2019-06-12
    打卡
    
    
  • sureingo
    2018-11-29
    老师您好,文章中提到互斥锁不能保证临界区内代码的原子性,我用github中demo60做了好多次试验,每次的结果都是正确的,能帮忙解释下吗

    type counter struct {
        num uint // 计数。
        mu sync.RWMutex // 读写锁。
    }

    // number 会返回当前的计数。
    func (c *counter) number() uint {
        c.mu.RLock()
        defer c.mu.RUnlock()
        return c.num
    }

    // add 会增加计数器的值,并会返回增加后的计数。
    func (c *counter) add(increment uint) uint {
        c.mu.Lock()
        defer c.mu.Unlock()
        c.num += increment
        return c.num
    }

    func main() {
        c := counter{}
        sign := make(chan struct{})
        for i := 0; i < 1000; i++ {
            go func() {
                defer func() {
                    sign <- struct{}{}
                }()
                for j := 0; j < 1000; j++ {
                    c.add(1)
                }
            }()
        }
        for i := 0; i < 1000; i++ {
            <-sign
        }
        fmt.Println(c.number())
    }
    输出是1000000
    展开
     3
    
  • 翅膀
    2018-11-15
    绕过需要一个中间变量
    
    
  • 任小样🏂
    2018-11-08
    楼上的,认真审题,可以绕过。但我的问题来了,这算BUG嘛?
    
    
  • MagicYang
    2018-11-04
    uint32(int32(-3)) 貌似绕不过
    
    
  • Askerlve
    2018-10-17
    go入门菜鸟,感激有这么一个专栏,学完就可以买一本go并发编程来撸了~😀
    
    
我们在线,来聊聊吧