<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: ThePeyman</title>
    <description>The latest articles on Forem by ThePeyman (@thepeyman).</description>
    <link>https://forem.com/thepeyman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F895250%2F7b1818bb-1b10-4750-a104-440edc3a3f8f.jpg</url>
      <title>Forem: ThePeyman</title>
      <link>https://forem.com/thepeyman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thepeyman"/>
    <language>en</language>
    <item>
      <title>Unlocking Go’s sync.Cond: The Dinner Bell Pattern</title>
      <dc:creator>ThePeyman</dc:creator>
      <pubDate>Mon, 29 Dec 2025 18:51:08 +0000</pubDate>
      <link>https://forem.com/thepeyman/unlocking-gos-synccond-the-dinner-bell-pattern-lci</link>
      <guid>https://forem.com/thepeyman/unlocking-gos-synccond-the-dinner-bell-pattern-lci</guid>
      <description>&lt;p&gt;If you ask a Go developer how to handle concurrency, they will almost certainly say: &lt;strong&gt;"Use Channels."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And 95% of the time, they are right. Channels are the idiomatic way to send data and signals between goroutines. But what about that other 5%? What happens when you need to broadcast a signal to 1,000 goroutines simultaneously without looping?&lt;/p&gt;

&lt;p&gt;Enter &lt;code&gt;sync.Cond&lt;/code&gt; (the Condition Variable)—Go’s most misunderstood concurrency primitive.&lt;/p&gt;

&lt;p&gt;In this post, I’ll explain &lt;code&gt;sync.Cond&lt;/code&gt; using a simple mental model: &lt;strong&gt;The Dinner Bell.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Polling vs. Signaling
&lt;/h2&gt;

&lt;p&gt;Imagine a father (The Publisher) making pancakes for his 10 hungry children (The Subscribers).&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: The Polling Loop (Bad)
&lt;/h3&gt;

&lt;p&gt;The children run into the kitchen every 5 seconds, check the plate, see it's empty, and leave.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;CPU:&lt;/strong&gt; High (Children are running back and forth).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Contention:&lt;/strong&gt; The kitchen door (Mutex) is constantly being locked and unlocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Approach 2: Channels (Okay, but Linear)
&lt;/h3&gt;

&lt;p&gt;The father finishes a pancake. He has to walk to each child individually and hand them a piece.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Latency:&lt;/strong&gt; The 10th child gets their food much later than the 1st.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Coupling:&lt;/strong&gt; The father is busy delivering instead of cooking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Approach 3: &lt;code&gt;sync.Cond&lt;/code&gt; (The Dinner Bell)
&lt;/h3&gt;

&lt;p&gt;The children sit at the table and &lt;strong&gt;fall asleep&lt;/strong&gt;.&lt;br&gt;
The father cooks a batch of pancakes, puts them on the table, and &lt;strong&gt;rings a loud bell&lt;/strong&gt; (&lt;code&gt;Broadcast&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Result:&lt;/strong&gt; Everyone wakes up instantly.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Efficiency:&lt;/strong&gt; Zero CPU usage while waiting.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How it Works in Code
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;sync.Cond&lt;/code&gt; object is always paired with a &lt;code&gt;sync.Mutex&lt;/code&gt; (the lock). This is the part that confuses most developers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of the &lt;strong&gt;Mutex&lt;/strong&gt; as the &lt;strong&gt;Key to the Kitchen&lt;/strong&gt;. You cannot check for food (Data) without the Key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"sync"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Kitchen&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;       &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
    &lt;span class="n"&gt;cond&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cond&lt;/span&gt;
    &lt;span class="n"&gt;pancakes&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewKitchen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Kitchen&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Kitchen&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="c"&gt;// We link the Cond to the Lock!&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Publisher (The Cook)
&lt;/h3&gt;

&lt;p&gt;The cook creates data and rings the bell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Kitchen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Cook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;         &lt;span class="c"&gt;// 1. Grab the Key&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pancakes&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;        &lt;span class="c"&gt;// 2. Make food&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pancake ready!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;       &lt;span class="c"&gt;// 3. Put Key back&lt;/span&gt;

    &lt;span class="c"&gt;// 4. RING THE BELL!&lt;/span&gt;
    &lt;span class="c"&gt;// Note: We don't need to hold the lock to broadcast,&lt;/span&gt;
    &lt;span class="c"&gt;// but it's often safer to do so.&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Broadcast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Subscriber (The Hungry Child)
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens. Look closely at the &lt;code&gt;Wait()&lt;/code&gt; call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Kitchen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// 1. Grab Key to enter kitchen&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 2. The Check Loop&lt;/span&gt;
    &lt;span class="c"&gt;// Why a loop? Because when you wake up, someone else might&lt;/span&gt;
    &lt;span class="c"&gt;// have eaten the pancake before you!&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pancakes&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// 3. WAIT&lt;/span&gt;
        &lt;span class="c"&gt;// This line does three things atomically:&lt;/span&gt;
        &lt;span class="c"&gt;// A. Unlocks the mutex (Drops the key).&lt;/span&gt;
        &lt;span class="c"&gt;// B. Suspends execution (Falls asleep).&lt;/span&gt;
        &lt;span class="c"&gt;// C. Locks the mutex (Grabs key) when woken up.&lt;/span&gt;
        &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 4. Eat&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pancakes&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Child %d ate a pancake.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The "Gotcha": Why does &lt;code&gt;Wait&lt;/code&gt; need a Lock?
&lt;/h2&gt;

&lt;p&gt;The most common question I get is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why do I have to pass the lock to &lt;code&gt;sync.NewCond&lt;/code&gt;? And why must I hold the lock before calling &lt;code&gt;Wait&lt;/code&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go back to the analogy.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;Wait()&lt;/code&gt; didn't drop the lock for you, you would fall asleep inside the kitchen with the door locked! The Cook would never be able to get in to make the food.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sync.Cond.Wait()&lt;/code&gt; performs a magic trick: it creates a safe point where you say, &lt;em&gt;"I am done with the lock for now, wake me up when something changes."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you use this?
&lt;/h2&gt;

&lt;p&gt;Don't abandon Channels yet. Use &lt;code&gt;sync.Cond&lt;/code&gt; only when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Multiple Readers:&lt;/strong&gt; You have many goroutines waiting for the &lt;em&gt;same&lt;/em&gt; signal.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;State-Based:&lt;/strong&gt; You are waiting for a specific condition (e.g., "Buffer is full", "Server is ready"), not just passing a value.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;High Frequency:&lt;/strong&gt; You want to avoid the overhead of creating/closing channels repeatedly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Channels&lt;/strong&gt; are for passing data. (Mailman)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mutexes&lt;/strong&gt; are for protecting data. (Lock and Key)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Conditions&lt;/strong&gt; are for signaling state changes. (Dinner Bell)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mastering &lt;code&gt;sync.Cond&lt;/code&gt; places you in the upper echelon of Go developers who understand that "Don't communicate by sharing memory" is a guideline, not a dogma. Sometimes, sharing memory (with the right locks) is exactly what you need for performance.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! If you have any war stories about &lt;code&gt;sync.Cond&lt;/code&gt; or deadlocks, let me know in the comments below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>concurrency</category>
      <category>backend</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
