<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Chapter 11 Synchronization Primitives and Patterns on Go: Under the Hood</title>
    <link>/ru/part3concurrency/ch11sync/</link>
    <description>Recent content in Chapter 11 Synchronization Primitives and Patterns on Go: Under the Hood</description>
    <generator>Hugo</generator>
    <language>ru</language>
    <atom:link href="/ru/part3concurrency/ch11sync/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>11.1 Shared-Memory Synchronization Patterns</title>
      <link>/ru/part3concurrency/ch11sync/basic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/basic/</guid>
      <description>&lt;h1 id=&#34;111-shared-memory-synchronization-patterns&#34;&gt;11.1 Shared-Memory Synchronization Patterns&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The fact that programs can be constructed from simpler basic primitives is a strong&#xA;guarantee: what these primitives contain stays logically consistent with the rest of the&#xA;programming language.&#xA;&amp;ndash; C. A. R. Hoare&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The whole difficulty of concurrent programming can be distilled into a single sentence:&#xA;multiple units of execution must cooperate over the same shared state, and the order in&#xA;which each of them makes progress cannot be predicted. How to make that cooperation both&#xA;correct and efficient has, over more than half a century, split into two great traditions.&#xA;This chapter is about one of them, shared memory. Before we take apart the concrete&#xA;primitives in Go&amp;rsquo;s standard library &lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;sync/atomic&lt;/code&gt;, this section first lays the&#xA;two traditions side by side to tell them apart, explains why they are dual to each other,&#xA;and then states where Go stands between them. Once you understand this layer of trade-offs,&#xA;the sections from &lt;a href=&#34;.././mutex&#34;&gt;11.2&lt;/a&gt; through &lt;a href=&#34;.././mem&#34;&gt;11.9&lt;/a&gt; are no longer isolated APIs&#xA;but the same design philosophy landing on different scenarios.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.2 Мьютекс</title>
      <link>/ru/part3concurrency/ch11sync/mutex/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/mutex/</guid>
      <description>&lt;h1 id=&#34;112-мьютекс&#34;&gt;11.2 Мьютекс&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;sync.Mutex&lt;/code&gt; — наиболее фундаментальный примитив синхронизации: он допускает в критическую секцию лишь одну горутину одновременно. За этим простым интерфейсом скрывается постоянное балансирование между двумя конкурирующими целями: &lt;strong&gt;пропускная способность&lt;/strong&gt; (передать блокировку как можно быстрее, не оставляя процессор простаивать) и &lt;strong&gt;справедливость&lt;/strong&gt; (не допускать бесконечного ожидания в очереди). Достичь обеих целей в полной мере невозможно: передача блокировки строго по порядку поступления наиболее справедлива, однако сопряжена с переключением контекста при каждой передаче; предоставление только что пробудившемуся ожидающему свободно конкурировать с уже выполняющимися горутинами — наиболее быстро, однако чревато тем, что некоторые ожидающие будут проигрывать снова и снова. Данный раздел сначала излагает теорию и аппаратные основы классической задачи взаимного исключения, а затем рассматривает, как мьютекс Go держит этот баланс.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.3 Атомарные операции</title>
      <link>/ru/part3concurrency/ch11sync/atomic/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/atomic/</guid>
      <description>&lt;h1 id=&#34;113-атомарные-операции&#34;&gt;11.3 Атомарные операции&lt;/h1&gt;&#xA;&lt;p&gt;&lt;code&gt;sync/atomic&lt;/code&gt; — это уровень примитивов синхронизации Go, расположенный ближе всего к аппаратуре.&#xA;Все более высокоуровневые инструменты — мьютекс (&lt;a href=&#34;.././mutex&#34;&gt;11.1&lt;/a&gt;) и каналы (&lt;a href=&#34;../ch08channel&#34;&gt;8&lt;/a&gt;) —&#xA;внутри построены на атомарных операциях. Пакет обеспечивает «неделимые» чтения, записи и операции&#xA;«чтение–модификация–запись»: атомарная операция либо выполняется целиком, либо не выполняется вовсе, и ни&#xA;одна горутина никогда не видит её в незавершённом состоянии. Однако смысл атомарных операций простирается&#xA;далеко за пределы «отсутствия частичной записи». Они составляют фундамент неблокирующего (lock-free)&#xA;программирования, а за неблокирующим программированием стоит глубокая теория о том, «какой примитив на что&#xA;способен». Именно эту теорию данный раздел стремится прояснить.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.4 Condition Variables</title>
      <link>/ru/part3concurrency/ch11sync/cond/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/cond/</guid>
      <description>&lt;h1 id=&#34;114-condition-variables&#34;&gt;11.4 Condition Variables&lt;/h1&gt;&#xA;&lt;p&gt;The mutex (&lt;a href=&#34;.././mutex&#34;&gt;11.2&lt;/a&gt;) answers the question of &amp;ldquo;who may enter the critical section.&amp;rdquo; A condition variable answers a different one: &amp;ldquo;a Goroutine has already entered the critical section, but finds it cannot do its work yet; how does it wait until the moment it can, while yielding the critical section to others.&amp;rdquo; The producer and consumer is the most common example. When the queue is full, the producer can neither write nor keep spinning while holding the lock, otherwise the consumer can never acquire the lock to free up space, and both sides deadlock together. The condition variable &lt;code&gt;sync.Cond&lt;/code&gt; offers this way out: let the producer &amp;ldquo;sleep with the lock,&amp;rdquo; atomically hand off the lock and block, and once the consumer frees up space, wake it back up, with the lock back in its hands on waking.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.5 Wait Groups</title>
      <link>/ru/part3concurrency/ch11sync/waitgroup/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/waitgroup/</guid>
      <description>&lt;h1 id=&#34;115-wait-groups&#34;&gt;11.5 Wait Groups&lt;/h1&gt;&#xA;&lt;p&gt;The mutex (&lt;a href=&#34;.././mutex&#34;&gt;11.2&lt;/a&gt;) and the condition variable (&lt;a href=&#34;.././cond&#34;&gt;11.4&lt;/a&gt;) from the previous&#xA;sections solve the problem of &amp;ldquo;multiple Goroutines contending for the same shared state.&amp;rdquo; This&#xA;section turns to &lt;code&gt;sync.WaitGroup&lt;/code&gt;, which solves a different class of problem: one Goroutine spawns&#xA;several subtasks, then waits for all of them to finish before continuing. The former is contention,&#xA;the latter is rendezvous. This is one of the most common structures in concurrent programming,&#xA;called fork-join, and &lt;code&gt;WaitGroup&lt;/code&gt; is exactly the Go standard library&amp;rsquo;s answer to it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.6 Object Pool</title>
      <link>/ru/part3concurrency/ch11sync/pool/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/pool/</guid>
      <description>&lt;h1 id=&#34;116-object-pool&#34;&gt;11.6 Object Pool&lt;/h1&gt;&#xA;&lt;p&gt;Allocating and then discarding the same kind of temporary object over and over puts heavy pressure on&#xA;the garbage collector (&lt;a href=&#34;/ru/part4memory/ch13gc/&#34;&gt;13&lt;/a&gt;). &lt;code&gt;sync.Pool&lt;/code&gt; offers a way out: stash a used object&#xA;and reuse it next time instead of allocating fresh every time. Its typical use is for temporary objects&#xA;like buffers and serializers that are &amp;ldquo;discarded after one use, yet needed again and again&amp;rdquo;.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bufPool&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Pool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bufPool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Reset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;           &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// the object you get back may be dirty, so reset it first&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ... use b ...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bufPool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// put it back when done, for others to reuse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;&lt;code&gt;New&lt;/code&gt; is the only field the user needs to supply: when the pool has no object to hand out, it falls back&#xA;to &lt;code&gt;New&lt;/code&gt; to make one. So what &lt;code&gt;Get&lt;/code&gt; returns is either an old object someone just put back, or a new one&#xA;freshly made by &lt;code&gt;New&lt;/code&gt;, and the caller has no way, and no need, to tell the two apart. This section first&#xA;covers the ancient idea of reusing objects that the pool rests on, then unpacks the three layers it builds&#xA;for concurrency and GC: per-P sharding, the victim cache, and cooperation with the runtime&amp;rsquo;s GC.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.7 Concurrency-Safe Hash Table</title>
      <link>/ru/part3concurrency/ch11sync/map/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/map/</guid>
      <description>&lt;h1 id=&#34;117-concurrency-safe-hash-table&#34;&gt;11.7 Concurrency-Safe Hash Table&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;code&gt;sync.Map&lt;/code&gt; went through a complete internal rewrite in Go 1.24: the original&#xA;read/dirty two-map design was replaced by a concurrent hash-trie. This section first makes the&#xA;origin of each of these two generations of design clear, then places them back into the&#xA;solution lineage of concurrent hash tables.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The language&amp;rsquo;s built-in &lt;code&gt;map&lt;/code&gt; is not concurrency-safe. When multiple goroutines read and write the same &lt;code&gt;map&lt;/code&gt; simultaneously without synchronization,&#xA;the runtime actively detects the concurrent access and terminates the process with &lt;code&gt;fatal error: concurrent map read and map write&lt;/code&gt;&#xA;(&lt;a href=&#34;/ru/part2lang/ch05data/map/&#34;&gt;5.2&lt;/a&gt; introduced this detection mechanism; it is unrecoverable, and &lt;code&gt;recover&lt;/code&gt;&#xA;cannot intercept it). For services with availability requirements, this is a design question that must be answered head-on: when multiple goroutines need to share&#xA;one table, what structure should be used.&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.8 Context</title>
      <link>/ru/part3concurrency/ch11sync/context/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/context/</guid>
      <description>&lt;h1 id=&#34;118-context&#34;&gt;11.8 Context&lt;/h1&gt;&#xA;&lt;p&gt;A request entering a server seldom runs to completion inside a single goroutine. It fans out into&#xA;a tree of goroutines: one queries the database, one calls a downstream RPC, one reads the cache,&#xA;and each of these spawns smaller units of work in turn. Once the originator of the request no longer&#xA;needs the result, say the client has disconnected, or the upstream has already timed out, all the&#xA;work still running across that tree becomes pure waste, holding connections, locks, and memory while&#xA;no one will ever read what it produces. The problem then becomes: &lt;strong&gt;how do we propagate the signal&#xA;&amp;ldquo;stop here&amp;rdquo; from the root of the tree out to every leaf, so each of them can stop on its own.&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>11.9 Memory Consistency Models</title>
      <link>/ru/part3concurrency/ch11sync/mem/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/ru/part3concurrency/ch11sync/mem/</guid>
      <description>&lt;h1 id=&#34;119-memory-consistency-models&#34;&gt;11.9 Memory Consistency Models&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Go&amp;rsquo;s memory model went through an important revision in 2022 with Go 1.19.&#xA;After laying out the current model, this section devotes space to the background of that&#xA;revision (see &lt;a href=&#34;/ru/part3concurrency/ch11sync/mem/#1198-the-evolution-of-the-design&#34;&gt;11.9.8&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The reader may have noticed that in the earlier discussion of the Go runtime and compiler we&#xA;kept steering clear of one topic: Go&amp;rsquo;s memory model. The avoidance was not without reason. To&#xA;explain it properly requires groundwork in concurrency, synchronization primitives, and even&#xA;the hardware layer, and only now is that groundwork in place. As the close of this chapter,&#xA;and as the book&amp;rsquo;s summary of Go&amp;rsquo;s synchronization primitives and patterns, we take up the&#xA;topic here and answer the question still unresolved in the reader&amp;rsquo;s mind: when two Goroutines&#xA;read and write the same variable at once, under what conditions is one side&amp;rsquo;s write&#xA;guaranteed to be seen by the other.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
