Is it thread-safe to have 1 goroutine as producer and n goroutines read the produced value (not consume it)?

blov · · 438 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;d say yes, but maybe there is a corner case I&#39;m not thinking of.</p> <hr/>**评论:**<br/><br/>albatr0s: <pre><p>If you are incrementing a shared variable you should use the <a href="https://golang.org/pkg/sync/atomic/" rel="nofollow">sync/atomic</a> package, or a mutex.</p> <p>Building your program using <code>go build -race</code> will also help to catch some problems. More info: <a href="https://golang.org/doc/articles/race_detector.html" rel="nofollow">https://golang.org/doc/articles/race_detector.html</a>.</p></pre>whizack: <pre><p>this for sure. I&#39;d add that depending on the complexity of the code you may find only building with -race insufficient. You may need to write a test that specifically exercises this code as intended and run the tests with -race as well to truly verify there are no races.</p> <p>go vet can help you avoid mistakes there as well. Like using/composing mutex incorrectly.</p></pre>jinjo_arch: <pre><p>You have a shared, mutable state. Therefore, your program is prone to race conditions without the use of mutex, etc.</p></pre>dbud: <pre><p>If you mean, the producer is basically just setting a variable and the consumers are reading the same var. </p> <p>You potentially need some kind memory fence to ensure you see updates, assuming you have multiple cores. One way to ensure you read the &#34;lastest&#34; value is to use the atomic package to write and read the values.</p> <p>If the variable is actually a struct or some other thing that can&#39;t be set atomically, you probably want a read/write lock to ensure you don&#39;t read a partially written value.</p> <p>If you actually just meant a channel, there&#39;s no way not to consume the value when reading it, so you can&#39;t really do it that way.</p></pre>fechan: <pre><blockquote> <p>You potentially need some kind memory fence to ensure you see updates, assuming you have multiple cores. One way to ensure you read the &#34;lastest&#34; value is to use the atomic package to write and read the values.</p> </blockquote> <p>Thanks. I knew I was missing something. Glad I asked!</p></pre>Vlir: <pre><p>I&#39;d recommend an atomic.Value if you don&#39;t want to lock</p></pre>Darth_Vaporizer: <pre><p>Yes, as long as the producers and consumers are communicating via a channel. </p></pre>Cvballa3g0: <pre><p>Try using a RWMutex. An RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. </p> <p><a href="http://golang.org/pkg/sync/#RWMutex" rel="nofollow">http://golang.org/pkg/sync/#RWMutex</a></p></pre>fechan: <pre><p>Does a RWMutex ensure that the variable I write to between <code>Lock</code> and <code>Unlock</code> is &#34;volatile&#34; and will be visible to other cores?</p></pre>Cvballa3g0: <pre><p>I&#39;m not able to answer that question with 100% certainty, but I did find a blog post that raises the concerns you mentioned. </p> <p><a href="http://blog.launchdarkly.com/golang-pearl-thread-safe-writes-and-double-checked-locking-in-go/" rel="nofollow">http://blog.launchdarkly.com/golang-pearl-thread-safe-writes-and-double-checked-locking-in-go/</a></p></pre>fakeNAcsgoPlayer: <pre><p>Yes, it will do it. The compiler will enforce this.</p> <p>Essentially readers will just block waiting for writer to complete, once writer is done, all readers can proceed.</p> <p>Since Go doesn&#39;t have a volatile like C to avoid compiler optimization, the mutex is your memory barrier. </p></pre>shovelpost: <pre><p>It sounds like a yes but always use the race detector <code>go build -race</code> to be sure.</p></pre>brianketelsen: <pre><p>yes, if they are all reading, and only the producer is writing, you&#39;re fine.</p></pre>peterbourgon: <pre><p>This is not correct. If a reader tries to read in the middle of a write, there is a data race.</p></pre>fechan: <pre><p>Thank you! :)</p> <p>The producer keeps adding onto the number, so I was thinking maybe there is possibility of an inconsistent state when updating.</p></pre>AgentSS: <pre><p>If the producer is writing the variable (&#34;adding&#34;), and you have consumers simultaneously reading from that variable, then you have a data race (&#34;two or more memory accesses, atleast one of which is a write&#34;), so you can certainly have inconsistent state where the number temporarily becomes something arbitrary (say a pointer).</p> <p>For instance, if the writing goroutine is low on registers, it might spill some unrelated data into the storage for that variable, and then restore from it later. It is safe to do this for race-free programs, because the compiler can reason that you are doing a write, meaning no one else should be looking at this variable. So the reading thread might temporarily be reading that unrelated spilled data (which could for instance be a vtable for an interface, or a boolean, or something else entirely).</p> <p>You can also have arbitrary memory corruption (but the example here would be much more complicated (and require a very sophisticated optimizing compiler)). </p> <p>Use sync/atomic and the race detector.</p></pre>fechan: <pre><blockquote> <p>For instance, if the writing goroutine is low on registers, it might spill some unrelated data into the storage for that variable, and then restore from it later.</p> </blockquote> <p>Can this even be true? What you&#39;re saying is that I should replace</p> <pre><code>obj.var += 1 </code></pre> <p>with </p> <pre><code>obj.varMtx.Lock() defer obj.var.Mtx.Unlock() obj.var += 1 </code></pre> <p>but nowhere do I declare that my variable <code>obj.var</code> as &#34;volatile&#34; (which is not featured in Go). I simply happen to write to it after locking a Mutex. So either the Go language checks for write ops after <code>Lock</code> under the hood (which would surprise me), or more likely the <code>Unlock</code> call syncs registers with variables, or what you say is incorrect and no variable space is treated as dumping storage.</p></pre>robbert229: <pre><p>To quote the spec.</p> <p>Within a single goroutine, reads and writes must behave as if they executed in the order specified by the program. That is, compilers and processors may reorder the reads and writes executed within a single goroutine only when the reordering does not change the behavior within that goroutine as defined by the language specification...</p> <p>Within a single goroutine, there is no concurrency, so the two definitions are equivalent: a read r observes the value written by the most recent write w to v. When multiple goroutines access a shared variable v, they must use synchronization events to establish happens-before conditions that ensure reads observe the desired writes.</p> <p>In <a href="https://golang.org/ref/mem" rel="nofollow">https://golang.org/ref/mem</a></p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

438 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传