Help me understand Go 1.9 sync.Map "LoadOrStore" method.

xuanbao · · 1198 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<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>&#34;LoadOrStore&#34; seems like a huge anti pattern: you&#39;re either loading, or your storing, you typically &#34;shouldn&#39;t&#34; be doing both or either in one operation. Can anyone explain the use case for an API like this? It&#39;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&#39;t exist. If I would just use <code>Store</code>, I couldn&#39;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&#39;re right. Thanks for adding the lock and unlock on the record in the pseudo-code too, it&#39;s an important thing to understand.</p></pre>JackOhBlades: <pre><p>So if you were to simply <code>Store</code> a value there&#39;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 &#39;got there first&#39;.</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&#39;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&#39;s my pleasure!</p></pre>megromby: <pre><p>I don&#39;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&#39;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&#39;s a convenience, and I suppose it may have some performance benefits if the constructor/function is expensive.</p></pre>FUZxxl: <pre><p>It&#39;s not really a convenience, it&#39;s a necessity. Try implementing <code>LoadOrStore()</code> yourself and you&#39;ll see.</p></pre>megromby: <pre><p><em>Somebody</em> implemented LoadOrStore, so <em>anybody</em> could implement LoadOrStore. It&#39;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&#39;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&#39;s what I am trying to say and that&#39;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&#39;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&#39;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&#39;s why <code>LoadOrStore</code> is necessary. You can&#39;t do the same thing without this function.</p></pre>

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

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