30 | 原子操作(下)
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
Go语言中的原子操作函数和`sync/atomic`包中的`atomic.Value`类型是并发编程中的重要概念。原子操作函数包括加法、比较并交换、加载、存储和交换等操作,而`atomic.Value`类型可以原子地存储和加载任意的值。在使用原子值时需要注意不应该再被复制,存储的第一个值决定了今后能且只能存储哪一个类型的值。文章提出了一些使用建议,如不要暴露原子变量给外界,不要传递原子值及其指针值,尽量不要在原子值中存储引用类型的值等。相对于原子操作函数,原子值类型的优势很明显,但它的使用规则也更多一些。总的来说,原子操作函数在某些场景下比互斥锁更适用,因为其执行速度更快,使用更简单,不涉及临界区选择和死锁等问题。但在选择原子值与互斥锁时需要仔细考量,需要牢记文章中提出的注意事项和使用建议。文章内容涉及了原子操作函数的用法、原理、对比和最佳实践,为读者提供了对并发编程中原子操作的全面了解。
《Go 语言核心 36 讲》,新⼈⾸单¥59
全部留言(18)
- 最新
- 精选
- Geek_f1933b郝老师,什么时候使用atomic.value呢,有没有具体的应用中的简单例子呢
作者回复: 其实需要保护非引用类型的值的时候都挺适用的。如果是引用类型的值的话,可能会起不到保护作用。因为我们修改的往往是这个值引用的那个底层值,而 atomic.Value 只会保护这个值本身。 例子的话...比如保护全局配置、同时保护一坨全局计数、保护 bit array,等等。
2020-01-1816 - rename我认为最重要的三点是 要操作的变量类型,操作频率和整体操作耗时。请郝大指教~
作者回复: 其实主要还是变量类型,原子操作在这块是严格的。能用原子就用原子。锁这个原语还是相对较重。
2018-10-2714 - 蜉蝣老师好,衍生问题 4 的回答不能明白。既然原子操作是不会被中断的,那么为什么还会出现 “如果写操作还没有进行完,读操作就来读了,那么就只能读到仅修改了一部分的值。” 这种写操作还没完进行完就有其他操作进来? 如果是因为多核多 CPU 的话,那是不是说,读的原子操作与写的原子操作是互斥?
作者回复: 原文中的“如果写操作还没有进行完,读操作就来读了,那么就只能读到仅修改了一部分的值。”这句话是对读写锁来说的。如果只锁写操作、不锁读操作就会发生这样的问题。 这种原子操作一般都是通过原生的CPU指令实现的,所以从底层保障了并发操作的安全性。即使CPU是多核的,甚至有多个CPU,只要主板等硬件方面没有问题,都是可以保障的。 最后,原子操作和互斥锁不是一个东西。原子操作用防中断的方式来保并发,而互斥锁用串行化多个操作的方式来保并发(它不防中断)。 你如果说的只是“互斥”这个词,那你也可以理解为针对同一个变量的原子操作是“互斥”的(但它是通过“防中断”来达到“互斥”的目的)。
2020-11-0628 - mkii一旦atomic.Value类型的值(以下简称原子值)被真正使用,它就不应该再被复制了。 老师,如果Value存储的是引用类型,被复制有可能绕过原子值进行非并发操作这个我可以理解。但如果Value存储的是值类型,如果复制了再对这个副本操作应该不会对原值有影响。这里是怕别人误操作产生歧义吗?(即,我明明通过copyValue.store改了值,但为什么没生效?)
作者回复: Value里面存储的是被操作值的指针啊,所以一样的。
2021-02-2532 - poettian看到衍生问题4我也有个疑惑,那如果不用原子操作,是不是说假如在写的同时有读的操作,我们读取变量实际读到的是个不完整的值?
作者回复: 是有这个可能的。
2021-01-102 - noisyes通过一个或多个公开的函数,让外界间接地使用到它。这种情况下不要把原子值传递到外界,不论是传递原子值本身还是它的指针值。 但是即使编写了一个函数,但是不传递原子值的话,怎么才可以让外界使用它呢。
作者回复: 靠复制啊,如果是值类型的值的话,从函数返回时就自动复制了,如果是引用类型的值的话,比如切片或字典,就需要手动复制。使用atomic.Value的时候一定要注意这类问题。
2022-06-231 - 风翱老师,关于热更有什么方案呢? 是否有使用过,并在实际的项目中运用过? 有没有相应的事例?
作者回复: 热更新的话,现在肯定是首选 context 啊,因为标准库中的其他包已经与 context 融合得非常好了。虽然现在市面上还有一些相关的第三方库,但是如果没有特殊需求的话,还还是推荐 context。
2022-06-1831 - jxs1211go的内置函数的源码是在哪里可以找到,比如说append()使用时,都做了什么
作者回复: 要看切片相关的话,先在源码 src 目录里找runtime/slice.go 。这个文件里有个 growslice 函数,append 会调用它。后边你就顺藤摸瓜吧。
2021-10-241 - lesserror郝林老师,demo64 中的 示例3 的第二条 打印: fmt.Printf("Store %d to box2.\n", v3) 是不是 应该 改为: fmt.Printf("Store %d to box3.\n", v3)
作者回复: 好像应该是 box3 ,回头我改一下吧,谢谢
2021-08-171 - Da Vinci在首次真正使用后,原子值就不应该再被复制了,这句话不是特别理解,想请老师再解释一下
作者回复: Value 类型的值里面一旦存了值就不应该再拷贝了,因为这很可能会让基于内存地址的互斥机制失效,并产生混乱。这也是 Value 的文档里特别说明的。
2019-11-11