Which data types are inherently atomic

huangapple go评论71阅读模式
英文:

Which data types are inherently atomic

问题

我正在翻译您的内容,请稍等片刻。

英文:

I was wondering which data types in Go are inherently thread safe (if any).

My assumption going in is that ints, floats and bools are safe and composite types are not.

  • Are my assumptions correct?
  • Are there different considerations with respect to mutating a composite type vs replacing it?
  • How about channels?

Thank you.

答案1

得分: 5

我不相信它们中的任何一个都是“保证”原子性的,但实际上可能有一些是原子性的(当然,原子性指的是从两个线程同时赋值给它们将产生其中一个值,而不是某个第三个值(例如每个值的位的组合)- 我们并不是指你可以原子性地比较和存储或类似的操作)。你最好查看一下Go内存模型

英文:

I don't believe that any of them are guaranteed to be atomic, but it's possible that some are in practice (of course, by atomic we mean that assigning to them simultaneously from two threads will produce one or the other value, and not some third value (such as a combination of the bits from each value) - we don't mean that you can atomically compare and store or something like that). Your best bet is to check out the Go Memory Model.

答案2

得分: 2

只是一个注意事项:

如果你在同一平台上编译和运行代码,那么改变单个字值通常应该是原子的。但是,除此之外,还有其他因素使得使用原子指令成为必须(即使你不打算使用更强的保证)。

原因是除了编译器级别的内存重排序之外,CPU级别的优化也可能导致重排序(写入不会立即传播到主内存,CPU具有存储缓冲区等)。

因此,在多线程环境中,你需要显式地使所有核心看到这些变化,否则会破坏内存。

至于改变复合类型,是的,你需要非常小心。最简单的方法是锁定整个对象(嵌入互斥锁是一个很好的方法,它对缓存更友好)。

否则,如果你想要原子地进行操作,你可以采用读-拷贝-更新的方法(查找RCU或写时复制),但是要小心!!除非你真的知道自己在做什么,否则很容易遇到麻烦。当你有可变对象嵌套到其他可变对象中时,很难做到这种事情(查找与嵌套无锁数据结构的线性化问题有关的问题)。这真的很棘手,我不建议这样做。即使你要添加额外的间接层,使得你的数据结构看起来是不可变的,解决并发原子读取、更新和删除的问题也是博士级别的东西。不过,如果你感兴趣,可以查找Aleksandar Prokopec的论文和论文:http://axel22.github.io/home/professional/

这就是为什么通道和互斥锁存在的原因。我期望在已经很好的通道性能上有进一步的性能改进。现在对于一些人来说,通道很容易理解,但我花了几天时间才真正理解它们。但一旦理解,使用它们就非常简单。

英文:

> Just a note :

Mutating a single word value should generally be atomic if you run the code on the same platform you compiled it on. But there is more to it that makes the use of atomic instructions compulsory (if you don't plan to use stronger guarantees even).

Reason is that besides memory reorderings at the compiler level, you might have reorderings at the CPU level for optimizations. (writes do not propagate immediately to main memory, CPU have store buffers etc).

So in a multithreaded environment, you need to explicitly make the changes visible to all cores or otherwise you will corrupt memory.

As far as mutating a composite type is concerned, yes, you need to be (VERY) careful. Easiest way is to lock the whole object (embedding a mutex is a good way to do it, it is more cache friendly).

Otherwise, if you want to do it atomically, you could adopt a Read-Copy-Update methodology (look up for RCU or Copy-on-Write) but BEWARE !! Unless you really know what you are doing you could get into trouble very very easily. It's difficult to do that sort of thing when you have mutable objects nested into other mutable objects (lookup for issues regarding linearizability of nested lock-free data structures). It is really tricky and I discourage it. Even if you were to add an extra level of indirection so that your datastructures look immutable, solving the issue of concurrent atomic reads, updates and deletes is PhD level stuff. If you are curious though, lookup for Aleksandar Prokopec's Thesis and papers : http://axel22.github.io/home/professional/

That's the reason channels and mutexes are here for you. And I expect incremental performance improvement on the already nice performance the channels have. Now channels are easy for some, but it took a few days for me to really wrap my head around them. But once done, it's really simple to use them.

huangapple
  • 本文由 发表于 2014年11月16日 04:00:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/26950096.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定