<p>I just read the Go 1.9 concurrent map <a href="https://tip.golang.org/pkg/sync/#Map" rel="nofollow">documentation</a> and the method <a href="https://tip.golang.org/pkg/sync/#Map.LoadOrStore" rel="nofollow">LoadOrStore</a> caught my attention.</p>
<p>"LoadOrStore" seems like a huge anti pattern: you're either loading, or your storing, you typically "shouldn't" be doing both or either in one operation. Can anyone explain the use case for an API like this? It's obviously a good enough use case for it to be in the 1.9 standard library.</p>
<p>Thoughts? :)</p>
<hr/>**评论:**<br/><br/>FUZxxl: <pre><p>This basically allows you to build an “insert or update” procedure which is often very useful.</p>
<p>For example, consider a database for some storage management software. The table maps items to how often they are in your storage room. When you get an item, you want to (a) create a new entry for this item type if this is the first item of its kind or (b) increment the number of items of this type. <code>LoadOrStore</code> allows you to achieve this behaviour: Prepare a blank item record, then <code>LoadOrStore</code> it. Now increment the number of items for the record <code>LoadOrStore</code> returned (of course, while locking that record). Without a <code>LoadOrStore</code> operation, this would be very hard to implement correctly.</p></pre>: <pre><p>[deleted]</p></pre>FUZxxl: <pre><p>Read my comment carefully. I use <code>LoadOrStore</code> in my example to initialize the record if it doesn't exist. If I would just use <code>Store</code>, I couldn't increment the counter in the record without a race condition otherwise. The pseudo code is roughly this:</p>
<ol>
<li>record ← map.LoadOrStore(item, blankRecord)</li>
<li>record.Lock()</li>
<li>record.Counter++</li>
<li>record.Unlock()</li>
</ol></pre>Redundancy_: <pre><p>Yes, you're right. Thanks for adding the lock and unlock on the record in the pseudo-code too, it's an important thing to understand.</p></pre>JackOhBlades: <pre><p>So if you were to simply <code>Store</code> a value there's a chance another goroutine may have beaten you to it during the <code>Store</code> operation, causing a data race. <code>LoadOrStore</code> would return that value (which, say, beat your <code>Store</code> by 1us) ensuring the value you want to increment is in fact the one that 'got there first'.</p>
<p>Is that correct?</p></pre>FUZxxl: <pre><p>Kinda. However, the only case where you actually want to <code>Store</code> in the example I gave is when this is the first item of its kind. So without <code>LoadOrStore</code>, the code would look something like this:</p>
<ol>
<li>check if <code>item</code> is already present</li>
<li>if it is, load the entry for <code>item</code></li>
<li>if it is not, store an empty record for <code>item</code></li>
<li>increment value for <code>item</code></li>
</ol>
<p>This doesn't work, just think what happens when two threads want to register the first item of a kind at the same time. One update would be lost.</p></pre>JackOhBlades: <pre><p>I get now! Thanks a lot FUZxxl.</p></pre>FUZxxl: <pre><p>It's my pleasure!</p></pre>megromby: <pre><p>I don't think in the intended use case you would be providing a value per se, but rather a function or a constructor. So if there's already a value stored for this key, return that value, otherwise construct a new value or obtain a new value by executing a function and store it. It's a convenience, and I suppose it may have some performance benefits if the constructor/function is expensive.</p></pre>FUZxxl: <pre><p>It's not really a convenience, it's a necessity. Try implementing <code>LoadOrStore()</code> yourself and you'll see.</p></pre>megromby: <pre><p><em>Somebody</em> implemented LoadOrStore, so <em>anybody</em> could implement LoadOrStore. It's <em>convenient</em> to have it in the standard lib, implemented well, rather than making everybody who wants that functionality have to implement it themselves, in a probably worse manner, or implement more cumbersome workarounds. Convenience is pretty much the definition of 80% or more of the things found in any standard lib.</p></pre>FUZxxl: <pre><p>You can only implement <code>LoadOrStore</code> using functionality not present in the interface (i.e. using details of the implementation). It's not possible to get the same effect as <code>LoadOrStore</code> using any of the other functions in the <code>Map</code> interface. That's what I am trying to say and that's why <code>LoadOrStore</code> is a necessity.</p></pre>megromby: <pre><p>I guess. You <em>can</em> achieve the same outcome, you just have to use a lock and some tests and if-then clauses. The way it's always been done before sync.map and LoadOrStore. I guess you could say LoadOrStore is a necessity if you want to do it as a one-liner in a safe manner—which, again, is the <em>definition</em> of a <em>convenience</em> function.</p></pre>FUZxxl: <pre><p>Yes—you'd have to use a lock over the whole map (or some other kind of external synchronization), defeating the entire purpose of <code>sync.Map</code>, namely, allowing <em>concurrent access</em> to the same map. That's why <code>LoadOrStore</code> is necessary. You can't do the same thing without this function.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
0 回复
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传