<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Once on Chen Shungen</title><link>https://chenshungen.cn/tags/once/</link><description>Recent content in Once on Chen Shungen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 20 Apr 2026 11:30:00 +0800</lastBuildDate><atom:link href="https://chenshungen.cn/tags/once/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 并发原语 - Once</title><link>https://chenshungen.cn/blog/golang-concurrency/golang-once/</link><pubDate>Mon, 20 Apr 2026 11:30:00 +0800</pubDate><guid>https://chenshungen.cn/blog/golang-concurrency/golang-once/</guid><description>&lt;p>前面几篇我们聊了 Mutex、WaitGroup 和 Cond，它们各自解决不同维度的并发问题。这一篇聊一个相对简单但极其实用的原语——&lt;code>sync.Once&lt;/code>，它解决的是 &lt;strong>&amp;ldquo;确保某个操作只执行一次&amp;rdquo;&lt;/strong> 的问题，最经典的场景就是单例资源的延迟初始化。&lt;/p>
&lt;h2 id="一为什么需要-once">一、为什么需要 Once？&lt;/h2>
&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">┌─────────────────────────────────────────────────────────────┐
&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">│ 1. package 级别变量 var startTime = time.Now() │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 2. init 函数 func init() { startTime = ... } │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 3. main 函数中调用 func main() { initApp() } │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ✅ 天然线程安全（Go 保证 init 串行执行） │
&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">┌─────────────────────────────────────────────────────────────┐
&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">│ ✅ 按需初始化，不浪费资源 │
&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;/code>&lt;/pre>&lt;/div>&lt;/div>
&lt;p>延迟初始化更灵活，但并发安全怎么办？最直接的想法是用 Mutex：&lt;/p></description></item></channel></rss>