<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>WaitGroup on Chen Shungen</title><link>https://chenshungen.cn/tags/waitgroup/</link><description>Recent content in WaitGroup on Chen Shungen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 20 Apr 2026 10:30:00 +0800</lastBuildDate><atom:link href="https://chenshungen.cn/tags/waitgroup/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 并发原语 - WaitGroup</title><link>https://chenshungen.cn/blog/golang-concurrency/golang-waitgroup/</link><pubDate>Mon, 20 Apr 2026 10:30:00 +0800</pubDate><guid>https://chenshungen.cn/blog/golang-concurrency/golang-waitgroup/</guid><description>&lt;p>上一篇我们聊了 Mutex，它解决的是&amp;quot;同一时刻只能有一个 goroutine 访问共享资源&amp;quot;的问题。但并发编程中还有另一类常见需求——&lt;strong>等待一组任务全部完成后再继续&lt;/strong>。这就是 &lt;code>sync.WaitGroup&lt;/code> 要解决的问题。&lt;/p>
&lt;h2 id="一为什么需要-waitgroup">一、为什么需要 WaitGroup？&lt;/h2>
&lt;p>假设你要并行执行三个子任务，全部完成后才能进入下一步。没有 WaitGroup 的话，你可能会这样写：&lt;/p>
&lt;div class="highlight-wrapper">
 &lt;button class="copy-code-btn" type="button" aria-label="Copy code to clipboard">
 &lt;svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
 &lt;rect x="9" y="9" width="13" height="13" rx="2" ry="2">&lt;/rect>
 &lt;path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">&lt;/path>
 &lt;/svg>
 &lt;span class="copy-text">Copy&lt;/span>
 &lt;/button>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// ❌ 轮询方案：又慢又浪费 CPU&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">done1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">done2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">done3&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">go&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="cm">/* 任务1 */&lt;/span> &lt;span class="nx">done1&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">}()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">go&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="cm">/* 任务2 */&lt;/span> &lt;span class="nx">done2&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">}()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">go&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="cm">/* 任务3 */&lt;/span> &lt;span class="nx">done3&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">}()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">done1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">done2&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">done3&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Sleep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="nx">time&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Millisecond&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">// 空转等待&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>
&lt;p>这种轮询方式有两个问题：&lt;/p>
&lt;div class="highlight-wrapper">
 &lt;button class="copy-code-btn" type="button" aria-label="Copy code to clipboard">
 &lt;svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
 &lt;rect x="9" y="9" width="13" height="13" rx="2" ry="2">&lt;/rect>
 &lt;path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">&lt;/path>
 &lt;/svg>
 &lt;span class="copy-text">Copy&lt;/span>
 &lt;/button>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">问题 1：响应慢 问题 2：浪费 CPU
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 任务完成 CPU
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ──●────────────┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> │ 等待轮询 │?│ │?│ │?│ │?│ │?│ ← 反复空问
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ───────────────● └─┘ └─┘ └─┘ └─┘ └─┘
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 最多延迟 100ms 全是无效检查&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>
&lt;p>WaitGroup 的方案则是：任务没完成就阻塞，全部完成后立即唤醒，零延迟、零空转。&lt;/p></description></item></channel></rss>