Consistently use Sleep in state-management examples

Consistently use `time.Sleep`, instead of `runtime.Gosched`, to ensure all
goroutines can make progress. `Gosched` wasn't working in the playground
(ref #149).

Also stop trying to compare operation rates. This was tenuous given e.g. how
short the programs ran for, and with the `Sleep`s we now expect the rates to
be similar anyways.
This commit is contained in:
Mark McGranaghan 2016-12-27 08:56:49 -08:00
parent 94cba6875d
commit 6a58750728
12 changed files with 87 additions and 85 deletions

View File

@ -10,7 +10,6 @@ package main
import "fmt"
import "time"
import "sync/atomic"
import "runtime"
func main() {
@ -30,8 +29,8 @@ func main() {
// `&` syntax.
atomic.AddUint64(&ops, 1)
// Allow other goroutines to proceed.
runtime.Gosched()
// Wait a bit between increments.
time.Sleep(time.Millisecond)
}
}()
}

View File

@ -1,2 +1,2 @@
ddfef8425eef64cfcb82799c9bddca4bfa9bbd29
2h8nvrnaHP
ce8821f1f4fd99d554ad6cde52403dd3b69bb70a
8p48eFFxDZ

View File

@ -1,7 +1,7 @@
# Running the program shows that we executed about
# 40,000 operations.
$ go run atomic-counters.go
ops: 40200
ops: 41419
# Next we'll look at mutexes, another tool for managing
# state.

View File

@ -8,7 +8,6 @@ package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
@ -22,13 +21,14 @@ func main() {
// This `mutex` will synchronize access to `state`.
var mutex = &sync.Mutex{}
// To compare the mutex-based approach with another
// we'll see later, `ops` will count how many
// operations we perform against the state.
var ops int64 = 0
// We'll keep track of how many read and write
// operations we do.
var readOps uint64 = 0
var writeOps uint64 = 0
// Here we start 100 goroutines to execute repeated
// reads against the state.
// reads against the state, once per millisecond in
// each goroutine.
for r := 0; r < 100; r++ {
go func() {
total := 0
@ -39,22 +39,15 @@ func main() {
// exclusive access to the `state`, read
// the value at the chosen key,
// `Unlock()` the mutex, and increment
// the `ops` count.
// the `readOps` count.
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddInt64(&ops, 1)
atomic.AddUint64(&readOps, 1)
// In order to ensure that this goroutine
// doesn't starve the scheduler, we explicitly
// yield after each operation with
// `runtime.Gosched()`. This yielding is
// handled automatically with e.g. every
// channel operation and for blocking
// calls like `time.Sleep`, but in this
// case we need to do it manually.
runtime.Gosched()
// Wait a bit between reads.
time.Sleep(time.Millisecond)
}
}()
}
@ -69,8 +62,8 @@ func main() {
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddInt64(&ops, 1)
runtime.Gosched()
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
@ -79,9 +72,11 @@ func main() {
// `mutex` for a second.
time.Sleep(time.Second)
// Take and report a final operations count.
opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
// Take and report final operation counts.
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
// With a final lock of `state`, show how it ended up.
mutex.Lock()

View File

@ -1,2 +1,2 @@
3ce8467418aa740ea6c930afac3985a943c76311
kZrod-Rkos
e82356cbb37143862b0a9bbc68856f4b272c4918
a9Wky7k-Bw

View File

@ -1,9 +1,10 @@
# Running the program shows that we executed about
# 3,500,000 operations against our `mutex`-synchronized
# 90,000 total operations against our `mutex`-synchronized
# `state`.
$ go run mutexes.go
ops: 3598302
state: map[1:38 4:98 2:23 3:85 0:44]
readOps: 83285
writeOps: 8320
state: map[1:97 4:53 0:33 2:15 3:2]
# Next we'll look at implementing this same state
# management task using only goroutines and channels.

View File

@ -37,7 +37,8 @@ type writeOp struct {
func main() {
// As before we'll count how many operations we perform.
var ops int64 = 0
var readOps uint64 = 0
var writeOps uint64 = 0
// The `reads` and `writes` channels will be used by
// other goroutines to issue read and write requests,
@ -80,7 +81,8 @@ func main() {
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddInt64(&ops, 1)
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
@ -96,7 +98,8 @@ func main() {
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddInt64(&ops, 1)
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
@ -104,7 +107,9 @@ func main() {
// Let the goroutines work for a second.
time.Sleep(time.Second)
// Finally, capture and report the `ops` count.
opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
// Finally, capture and report the op counts.
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}

View File

@ -1,2 +1,2 @@
603a70a77ed18db9da4f8c7911a92b87fd21400c
-WqmiTr6ek
c306add52c0752f0b3099eb669364fc4bdb74be1
P4SrrlosMp

View File

@ -1,8 +1,9 @@
# Running our program shows that the goroutine-based
# state management example achieves about 800,000
# operations per second.
# state management example completes about 80,000
# total operations.
$ go run stateful-goroutines.go
ops: 807434
readOps: 71708
writeOps: 7177
# For this particular case the goroutine-based approach
# was a bit more involved than the mutex-based one. It

View File

@ -44,7 +44,7 @@ counters</em> accessed by multiple goroutines.</p>
</td>
<td class="code leading">
<a href="http://play.golang.org/p/2h8nvrnaHP"><img title="Run code" src="play.png" class="run" /></a>
<a href="http://play.golang.org/p/8p48eFFxDZ"><img title="Run code" src="play.png" class="run" /></a>
<div class="highlight"><pre><span class="kn">package</span> <span class="nx">main</span>
</pre></div>
@ -60,7 +60,6 @@ counters</em> accessed by multiple goroutines.</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="s">&quot;fmt&quot;</span>
<span class="kn">import</span> <span class="s">&quot;time&quot;</span>
<span class="kn">import</span> <span class="s">&quot;sync/atomic&quot;</span>
<span class="kn">import</span> <span class="s">&quot;runtime&quot;</span>
</pre></div>
</td>
@ -127,12 +126,12 @@ address of our <code>ops</code> counter with the
<tr>
<td class="docs">
<p>Allow other goroutines to proceed.</p>
<p>Wait a bit between increments.</p>
</td>
<td class="code leading">
<div class="highlight"><pre> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Gosched</span><span class="p">()</span>
<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">Millisecond</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}()</span>
<span class="p">}</span>
@ -187,7 +186,7 @@ fetch the value.</p>
<td class="code leading">
<div class="highlight"><pre><span class="gp">$</span> go run atomic-counters.go
<span class="go">ops: 40200</span>
<span class="go">ops: 41419</span>
</pre></div>
</td>

View File

@ -42,7 +42,7 @@ to safely access data across multiple goroutines.</p>
</td>
<td class="code leading">
<a href="http://play.golang.org/p/kZrod-Rkos"><img title="Run code" src="play.png" class="run" /></a>
<a href="http://play.golang.org/p/a9Wky7k-Bw"><img title="Run code" src="play.png" class="run" /></a>
<div class="highlight"><pre><span class="kn">package</span> <span class="nx">main</span>
</pre></div>
@ -58,7 +58,6 @@ to safely access data across multiple goroutines.</p>
<div class="highlight"><pre><span class="kn">import</span> <span class="p">(</span>
<span class="s">&quot;fmt&quot;</span>
<span class="s">&quot;math/rand&quot;</span>
<span class="s">&quot;runtime&quot;</span>
<span class="s">&quot;sync&quot;</span>
<span class="s">&quot;sync/atomic&quot;</span>
<span class="s">&quot;time&quot;</span>
@ -108,14 +107,14 @@ to safely access data across multiple goroutines.</p>
<tr>
<td class="docs">
<p>To compare the mutex-based approach with another
we&rsquo;ll see later, <code>ops</code> will count how many
operations we perform against the state.</p>
<p>We&rsquo;ll keep track of how many read and write
operations we do.</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>
<div class="highlight"><pre> <span class="kd">var</span> <span class="nx">readOps</span> <span class="kt">uint64</span> <span class="p">=</span> <span class="mi">0</span>
<span class="kd">var</span> <span class="nx">writeOps</span> <span class="kt">uint64</span> <span class="p">=</span> <span class="mi">0</span>
</pre></div>
</td>
@ -124,7 +123,8 @@ operations we perform against the state.</p>
<tr>
<td class="docs">
<p>Here we start 100 goroutines to execute repeated
reads against the state.</p>
reads against the state, once per millisecond in
each goroutine.</p>
</td>
<td class="code leading">
@ -145,7 +145,7 @@ reads against the state.</p>
exclusive access to the <code>state</code>, read
the value at the chosen key,
<code>Unlock()</code> the mutex, and increment
the <code>ops</code> count.</p>
the <code>readOps</code> count.</p>
</td>
<td class="code leading">
@ -154,7 +154,7 @@ the <code>ops</code> count.</p>
<span class="nx">mutex</span><span class="p">.</span><span class="nx">Lock</span><span class="p">()</span>
<span class="nx">total</span> <span class="o">+=</span> <span class="nx">state</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span>
<span class="nx">mutex</span><span class="p">.</span><span class="nx">Unlock</span><span class="p">()</span>
<span class="nx">atomic</span><span class="p">.</span><span class="nx">AddInt64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nx">atomic</span><span class="p">.</span><span class="nx">AddUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">readOps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</pre></div>
</td>
@ -162,19 +162,12 @@ the <code>ops</code> count.</p>
<tr>
<td class="docs">
<p>In order to ensure that this goroutine
doesn&rsquo;t starve the scheduler, we explicitly
yield after each operation with
<code>runtime.Gosched()</code>. This yielding is
handled automatically with e.g. every
channel operation and for blocking
calls like <code>time.Sleep</code>, but in this
case we need to do it manually.</p>
<p>Wait a bit between reads.</p>
</td>
<td class="code leading">
<div class="highlight"><pre> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Gosched</span><span class="p">()</span>
<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">Millisecond</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}()</span>
<span class="p">}</span>
@ -199,8 +192,8 @@ using the same pattern we did for reads.</p>
<span class="nx">mutex</span><span class="p">.</span><span class="nx">Lock</span><span class="p">()</span>
<span class="nx">state</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="p">=</span> <span class="nx">val</span>
<span class="nx">mutex</span><span class="p">.</span><span class="nx">Unlock</span><span class="p">()</span>
<span class="nx">atomic</span><span class="p">.</span><span class="nx">AddInt64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nx">runtime</span><span class="p">.</span><span class="nx">Gosched</span><span class="p">()</span>
<span class="nx">atomic</span><span class="p">.</span><span class="nx">AddUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">writeOps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<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">Millisecond</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}()</span>
<span class="p">}</span>
@ -225,13 +218,15 @@ using the same pattern we did for reads.</p>
<tr>
<td class="docs">
<p>Take and report a final operations count.</p>
<p>Take and report final operation counts.</p>
</td>
<td class="code leading">
<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">&amp;</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">&quot;ops:&quot;</span><span class="p">,</span> <span class="nx">opsFinal</span><span class="p">)</span>
<div class="highlight"><pre> <span class="nx">readOpsFinal</span> <span class="o">:=</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">LoadUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">readOps</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">&quot;readOps:&quot;</span><span class="p">,</span> <span class="nx">readOpsFinal</span><span class="p">)</span>
<span class="nx">writeOpsFinal</span> <span class="o">:=</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">LoadUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">writeOps</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">&quot;writeOps:&quot;</span><span class="p">,</span> <span class="nx">writeOpsFinal</span><span class="p">)</span>
</pre></div>
</td>
@ -260,15 +255,16 @@ using the same pattern we did for reads.</p>
<tr>
<td class="docs">
<p>Running the program shows that we executed about
3,500,000 operations against our <code>mutex</code>-synchronized
90,000 total operations against our <code>mutex</code>-synchronized
<code>state</code>.</p>
</td>
<td class="code leading">
<div class="highlight"><pre><span class="gp">$</span> go run mutexes.go
<span class="go">ops: 3598302</span>
<span class="go">state: map[1:38 4:98 2:23 3:85 0:44]</span>
<span class="go">readOps: 83285</span>
<span class="go">writeOps: 8320</span>
<span class="go">state: map[1:97 4:53 0:33 2:15 3:2]</span>
</pre></div>
</td>

View File

@ -46,7 +46,7 @@ by exactly 1 goroutine.</p>
</td>
<td class="code leading">
<a href="http://play.golang.org/p/-WqmiTr6ek"><img title="Run code" src="play.png" class="run" /></a>
<a href="http://play.golang.org/p/P4SrrlosMp"><img title="Run code" src="play.png" class="run" /></a>
<div class="highlight"><pre><span class="kn">package</span> <span class="nx">main</span>
</pre></div>
@ -117,7 +117,8 @@ goroutine to respond.</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>
<div class="highlight"><pre> <span class="kd">var</span> <span class="nx">readOps</span> <span class="kt">uint64</span> <span class="p">=</span> <span class="mi">0</span>
<span class="kd">var</span> <span class="nx">writeOps</span> <span class="kt">uint64</span> <span class="p">=</span> <span class="mi">0</span>
</pre></div>
</td>
@ -190,7 +191,8 @@ result over the provided <code>resp</code> channel.</p>
<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">&lt;-</span> <span class="nx">read</span>
<span class="o">&lt;-</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">&amp;</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nx">atomic</span><span class="p">.</span><span class="nx">AddUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">readOps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<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">Millisecond</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}()</span>
<span class="p">}</span>
@ -216,7 +218,8 @@ approach.</p>
<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">&lt;-</span> <span class="nx">write</span>
<span class="o">&lt;-</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">&amp;</span><span class="nx">ops</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nx">atomic</span><span class="p">.</span><span class="nx">AddUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">writeOps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<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">Millisecond</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}()</span>
<span class="p">}</span>
@ -240,13 +243,15 @@ approach.</p>
<tr>
<td class="docs">
<p>Finally, capture and report the <code>ops</code> count.</p>
<p>Finally, capture and report the op counts.</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">&amp;</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">&quot;ops:&quot;</span><span class="p">,</span> <span class="nx">opsFinal</span><span class="p">)</span>
<div class="highlight"><pre> <span class="nx">readOpsFinal</span> <span class="o">:=</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">LoadUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">readOps</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">&quot;readOps:&quot;</span><span class="p">,</span> <span class="nx">readOpsFinal</span><span class="p">)</span>
<span class="nx">writeOpsFinal</span> <span class="o">:=</span> <span class="nx">atomic</span><span class="p">.</span><span class="nx">LoadUint64</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">writeOps</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">&quot;writeOps:&quot;</span><span class="p">,</span> <span class="nx">writeOpsFinal</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
@ -260,14 +265,15 @@ approach.</p>
<tr>
<td class="docs">
<p>Running our program shows that the goroutine-based
state management example achieves about 800,000
operations per second.</p>
state management example completes about 80,000
total operations.</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>
<span class="go">readOps: 71708</span>
<span class="go">writeOps: 7177</span>
</pre></div>
</td>