2019-10-14 23:20:44 +03:00

306 lines
16 KiB
Plaintext
Generated
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go в примерах: Мьютексы (Mutexes)</title>
<link rel=stylesheet href="site.css">
</head>
<script>
onkeydown = (e) => {
if (e.key == "ArrowLeft") {
window.location.href = 'atomic-counters';
}
if (e.key == "ArrowRight") {
window.location.href = 'stateful-goroutines';
}
}
</script>
<body>
<div class="example" id="mutexes">
<h2><a href="./">Go в примерах</a>: Мьютексы (Mutexes)</h2>
<table>
<tr>
<td class="docs">
<p>В предыдущем примере мы увидели, как управлять простым
состоянием счетчика с помощью <a href="atomic-counters">атомарных операций</a>.
Для более сложного состояния мы можем использовать <em>мьютекс(<a href="http://en.wikipedia.org/wiki/Mutual_exclusion">http://en.wikipedia.org/wiki/Mutual_exclusion</a>)</em>
для безопасного доступа к данным в нескольких горутинах.</p>
</td>
<td class="code empty leading">
</td>
</tr>
<tr>
<td class="docs">
</td>
<td class="code leading">
<a href="http://play.golang.org/p/USO1aTX_xMe" target="_blank"><img title="Run code" src="play.png" class="run" /></a><img title="Copy code" src="clipboard.png" class="copy" />
<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">&quot;fmt&quot;</span>
<span class="s">&quot;math/rand&quot;</span>
<span class="s">&quot;sync&quot;</span>
<span class="s">&quot;sync/atomic&quot;</span>
<span class="s">&quot;time&quot;</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>Для нашего примера <code>state</code> будет картой.</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>Этот <code>mutex</code> будет синхронизировать доступ к <code>state</code>.</p>
</td>
<td class="code leading">
<div class="highlight"><pre> <span class="kd">var</span> <span class="nx">mutex</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span><span class="p">{}</span>
</pre></div>
</td>
</tr>
<tr>
<td class="docs">
<p>Мы будем отслеживать, сколько операций чтения и
записи мы выполняем.</p>
</td>
<td class="code leading">
<div class="highlight"><pre> <span class="kd">var</span> <span class="nx">readOps</span> <span class="kt">uint64</span>
<span class="kd">var</span> <span class="nx">writeOps</span> <span class="kt">uint64</span>
</pre></div>
</td>
</tr>
<tr>
<td class="docs">
<p>Здесь мы запускаем 100 горутин для выполнения
повторных операций чтения по состоянию, один раз
в миллисекунду в каждой горутине.</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">&lt;</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="nx">total</span> <span class="o">:=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="p">{</span>
</pre></div>
</td>
</tr>
<tr>
<td class="docs">
<p>Для каждого чтения мы выбираем ключ для
доступа, блокируем <code>mutex</code> с помощью <code>Lock()</code> ,
чтобы обеспечить исключительный доступ к
<code>состоянию</code>, читаем значение в выбранном ключе,
разблокируем мьютекс <code>Unlock()</code> и увеличиваем
количество <code>readOps</code>.</p>
</td>
<td class="code leading">
<div class="highlight"><pre> <span class="nx">key</span> <span class="o">:=</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">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">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>
</tr>
<tr>
<td class="docs">
<p>Немного ждем между чтениями.</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">Millisecond</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>Запустим так же 10 горутин для симуляции записи,
так же как мы делали для чтения.</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">&lt;</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">key</span> <span class="o">:=</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="o">:=</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">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">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>
</pre></div>
</td>
</tr>
<tr>
<td class="docs">
<p>Пусть 10 горутин работают над <code>состоянием</code> и
<code>мьютексом</code> на секунду.</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>Смотрим финальное количество операций</p>
</td>
<td class="code leading">
<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>
</tr>
<tr>
<td class="docs">
<p>С окончательной блокировкой <code>состояния</code> смотрим,
как все закончилось.</p>
</td>
<td class="code">
<div class="highlight"><pre> <span class="nx">mutex</span><span class="p">.</span><span class="nx">Lock</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;state:&quot;</span><span class="p">,</span> <span class="nx">state</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="p">}</span>
</pre></div>
</td>
</tr>
</table>
<table>
<tr>
<td class="docs">
<p>Запуск программы показывает, что мы выполнили
около 90000 операций в нашем синхронизированном
с <code>мьютексом</code>состоянии.</p>
</td>
<td class="code leading">
<div class="highlight"><pre><span class="gp">$</span> go run mutexes.go
<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>
</tr>
<tr>
<td class="docs">
<p>Далее мы рассмотрим реализацию той же задачи управления
состоянием с использованием только горутин и каналов.</p>
</td>
<td class="code empty">
</td>
</tr>
</table>
<p class="next">
Следующий пример: <a href="stateful-goroutines">Управление состоянием горутин (Stateful Goroutines)</a>.
</p>
<p class="footer">
by <a href="https://markmcgranaghan.com">Mark McGranaghan</a> | <a href="https://github.com/mmcgrana/gobyexample/blob/master/examples/mutexes">source</a> | <a href="https://github.com/mmcgrana/gobyexample#license">license</a>
</p>
</div>
<script>
var codeLines = [];
codeLines.push('');codeLines.push('package main\u000A');codeLines.push('import (\u000A \"fmt\"\u000A \"math/rand\"\u000A \"sync\"\u000A \"sync/atomic\"\u000A \"time\"\u000A)\u000A');codeLines.push('func main() {\u000A');codeLines.push(' var state = make(map[int]int)\u000A');codeLines.push(' var mutex = &sync.Mutex{}\u000A');codeLines.push(' var readOps uint64\u000A var writeOps uint64\u000A');codeLines.push(' for r := 0; r \x3C 100; r++ {\u000A go func() {\u000A total := 0\u000A for {\u000A');codeLines.push(' key := rand.Intn(5)\u000A mutex.Lock()\u000A total += state[key]\u000A mutex.Unlock()\u000A atomic.AddUint64(&readOps, 1)\u000A');codeLines.push(' time.Sleep(time.Millisecond)\u000A }\u000A }()\u000A }\u000A');codeLines.push(' for w := 0; w \x3C 10; w++ {\u000A go func() {\u000A for {\u000A key := rand.Intn(5)\u000A val := rand.Intn(100)\u000A mutex.Lock()\u000A state[key] = val\u000A mutex.Unlock()\u000A atomic.AddUint64(&writeOps, 1)\u000A time.Sleep(time.Millisecond)\u000A }\u000A }()\u000A }\u000A');codeLines.push(' time.Sleep(time.Second)\u000A');codeLines.push(' readOpsFinal := atomic.LoadUint64(&readOps)\u000A fmt.Println(\"readOps:\", readOpsFinal)\u000A writeOpsFinal := atomic.LoadUint64(&writeOps)\u000A fmt.Println(\"writeOps:\", writeOpsFinal)\u000A');codeLines.push(' mutex.Lock()\u000A fmt.Println(\"state:\", state)\u000A mutex.Unlock()\u000A}\u000A');codeLines.push('');codeLines.push('');
</script>
<script src="site.js" async></script>
</body>
</html>