Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta http-eqiv="content-type" content="text/html;charset=utf-8"> | |
| <title>Go by Example: Stateful Goroutines</title> | |
| <link rel=stylesheet href="site.css"> | |
| </head> | |
| <script type="text/javascript"> | |
| if (window.location.host == "gobyexample.com") { | |
| var _gaq = _gaq || []; | |
| _gaq.push(['_setAccount', 'UA-34996217-1']); | |
| _gaq.push(['_trackPageview']); | |
| (function() { | |
| var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | |
| ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | |
| var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | |
| })(); | |
| } | |
| </script> | |
| <body> | |
| <div class="example" id="stateful-goroutines"> | |
| <h2><a href="./">Go by Example</a>: Stateful Goroutines</h2> | |
| <table> | |
| <tr> | |
| <td class="docs"> | |
| <p>In the previous example we used explicit locking with | |
| mutexes to synchronize access to shared state across | |
| multiple goroutines. Another option is to use the | |
| built-in synchronization features of goroutines and | |
| channels to achieve the same result. This channel-based | |
| approach aligns with Go’s ideas of sharing memory by | |
| communicating and having each piece of data owned | |
| by exactly 1 goroutine.</p> | |
| </td> | |
| <td class="code empty leading"> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre><span class="kn">package</span> <span class="nx">main</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre><span class="kn">import</span> <span class="p">(</span> | |
| <span class="s">"fmt"</span> | |
| <span class="s">"math/rand"</span> | |
| <span class="s">"sync/atomic"</span> | |
| <span class="s">"time"</span> | |
| <span class="p">)</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>In this example our state will be owned by a single | |
| goroutine. This will guarantee that the data is never | |
| corrupted with concurrent access. In order to read or | |
| write that state, other goroutines will send messages | |
| to the owning goroutine and receive corresponding | |
| replies. These <code>readOp</code> and <code>writeOp</code> <code>struct</code>s | |
| encapsulate those requests and a way for the owning | |
| goroutine to respond.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre><span class="kd">type</span> <span class="nx">readOp</span> <span class="kd">struct</span> <span class="p">{</span> | |
| <span class="nx">key</span> <span class="kt">int</span> | |
| <span class="nx">resp</span> <span class="kd">chan</span> <span class="kt">int</span> | |
| <span class="p">}</span> | |
| <span class="kd">type</span> <span class="nx">writeOp</span> <span class="kd">struct</span> <span class="p">{</span> | |
| <span class="nx">key</span> <span class="kt">int</span> | |
| <span class="nx">val</span> <span class="kt">int</span> | |
| <span class="nx">resp</span> <span class="kd">chan</span> <span class="kt">bool</span> | |
| <span class="p">}</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre><span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>The <code>state</code> will be a map as in the previous | |
| example.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="kd">var</span> <span class="nx">state</span> <span class="p">=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">int</span><span class="p">]</span><span class="kt">int</span><span class="p">)</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>Also as before we’ll count how many operations we | |
| perform.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="kd">var</span> <span class="nx">ops</span> <span class="kt">int64</span> <span class="p">=</span> <span class="mi">0</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>The <code>reads</code> and <code>writes</code> channels will be used by | |
| other goroutines to issue read and write requests, | |
| respectively.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="nx">reads</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="o">*</span><span class="nx">readOp</span><span class="p">)</span> | |
| <span class="nx">writes</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="o">*</span><span class="nx">writeOp</span><span class="p">)</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>Here is the goroutine that owns the <code>state</code>. This | |
| goroutine repeatedly selects on the <code>reads</code> and | |
| <code>writes</code> channels, responding to requests as they | |
| arrive. A response is executed by first performing | |
| the requested operation and then sending a value | |
| on the response channel <code>resp</code> to indicate success | |
| (and the desired value in the case of <code>reads</code>).</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> | |
| <span class="k">for</span> <span class="p">{</span> | |
| <span class="k">select</span> <span class="p">{</span> | |
| <span class="k">case</span> <span class="nx">read</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">reads</span><span class="p">:</span> | |
| <span class="nx">read</span><span class="p">.</span><span class="nx">resp</span> <span class="o"><-</span> <span class="nx">state</span><span class="p">[</span><span class="nx">read</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span> | |
| <span class="k">case</span> <span class="nx">write</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">writes</span><span class="p">:</span> | |
| <span class="nx">state</span><span class="p">[</span><span class="nx">write</span><span class="p">.</span><span class="nx">key</span><span class="p">]</span> <span class="p">=</span> <span class="nx">write</span><span class="p">.</span><span class="nx">val</span> | |
| <span class="nx">write</span><span class="p">.</span><span class="nx">resp</span> <span class="o"><-</span> <span class="kc">true</span> | |
| <span class="p">}</span> | |
| <span class="p">}</span> | |
| <span class="p">}()</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>This starts 100 goroutines to issue reads to the | |
| state-owning goroutine via the <code>reads</code> channel. | |
| Each read requires constructing a <code>readOp</code>, sending | |
| it over the <code>reads</code> channel, and the receiving the | |
| result over the provided <code>resp</code> channel.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="k">for</span> <span class="nx">r</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">r</span> <span class="p"><</span> <span class="mi">100</span><span class="p">;</span> <span class="nx">r</span><span class="o">++</span> <span class="p">{</span> | |
| <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> | |
| <span class="k">for</span> <span class="p">{</span> | |
| <span class="nx">read</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">readOp</span><span class="p">{</span> | |
| <span class="nx">key</span><span class="p">:</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Intn</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> | |
| <span class="nx">resp</span><span class="p">:</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">int</span><span class="p">)}</span> | |
| <span class="nx">reads</span> <span class="o"><-</span> <span class="nx">read</span> | |
| <span class="o"><-</span><span class="nx">read</span><span class="p">.</span><span class="nx">resp</span> | |
| <span class="nx">atomic</span><span class="p">.</span><span class="nx">AddInt64</span><span class="p">(</span><span class="o">&</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> | |
| <span class="p">}</span> | |
| <span class="p">}()</span> | |
| <span class="p">}</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>We start 10 writes as well, using a similar | |
| approach.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="k">for</span> <span class="nx">w</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">w</span> <span class="p"><</span> <span class="mi">10</span><span class="p">;</span> <span class="nx">w</span><span class="o">++</span> <span class="p">{</span> | |
| <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> | |
| <span class="k">for</span> <span class="p">{</span> | |
| <span class="nx">write</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">writeOp</span><span class="p">{</span> | |
| <span class="nx">key</span><span class="p">:</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Intn</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> | |
| <span class="nx">val</span><span class="p">:</span> <span class="nx">rand</span><span class="p">.</span><span class="nx">Intn</span><span class="p">(</span><span class="mi">100</span><span class="p">),</span> | |
| <span class="nx">resp</span><span class="p">:</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">bool</span><span class="p">)}</span> | |
| <span class="nx">writes</span> <span class="o"><-</span> <span class="nx">write</span> | |
| <span class="o"><-</span><span class="nx">write</span><span class="p">.</span><span class="nx">resp</span> | |
| <span class="nx">atomic</span><span class="p">.</span><span class="nx">AddInt64</span><span class="p">(</span><span class="o">&</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> | |
| <span class="p">}</span> | |
| <span class="p">}()</span> | |
| <span class="p">}</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>Let the goroutines work for a second.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre> <span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>Finally, capture and report the <code>ops</code> count.</p> | |
| </td> | |
| <td class="code"> | |
| <div class="highlight"><pre> <span class="nx">opsFinal</span> <span class="o">:=</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">LoadInt64</span><span class="p">(</span><span class="o">&</span><span class="nx">ops</span><span class="p">)</span> | |
| <span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">"ops:"</span><span class="p">,</span> <span class="nx">opsFinal</span><span class="p">)</span> | |
| <span class="p">}</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| </table> | |
| <table> | |
| <tr> | |
| <td class="docs"> | |
| <p>Running our program shows that the goroutine-based | |
| state management example achieves about 800,000 | |
| operations per second.</p> | |
| </td> | |
| <td class="code leading"> | |
| <div class="highlight"><pre><span class="gp">$</span> go run stateful-goroutines.go | |
| <span class="go">ops: 807434</span> | |
| </pre></div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td class="docs"> | |
| <p>For this particular case the goroutine-based approach | |
| was a bit more involved than the mutex-based one. It | |
| might be useful in certain cases though, for example | |
| where you have other channels involved or when managing | |
| multiple such mutexes would be error-prone. You should | |
| use whichever approach feels most natural, especially | |
| with respect to understanding the correctness of your | |
| program.</p> | |
| </td> | |
| <td class="code empty"> | |
| </td> | |
| </tr> | |
| </table> | |
| <p class="next"> | |
| Next example: <a href="sorting">Sorting</a>. | |
| </p> | |
| <p class="footer"> | |
| <a href="https://twitter.com/gobyexample">@gobyexample</a> | <a href="mailto:mmcgrana@gmail.com">feedback</a> | <a href="https://github.com/mmcgrana/gobyexample/blob/master/examples/stateful-goroutines">source</a> | <a href="https://github.com/mmcgrana/gobyexample#license">license</a> | |
| </p> | |
| </div> | |
| </body> | |
| </html> |