<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Clay 的技术空间</title>
  
  <subtitle>用进废退 | 艺不压身</subtitle>
  <link href="https://www.techgrow.cn/atom.xml" rel="self"/>
  
  <link href="https://www.techgrow.cn/"/>
  <updated>2026-06-15T13:55:33.000Z</updated>
  <id>https://www.techgrow.cn/</id>
  
  <author>
    <name>Clay</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Debian 13 通过 Shell 脚本实现磁盘监控报警</title>
    <link href="https://www.techgrow.cn/posts/b58c3ba0.html"/>
    <id>https://www.techgrow.cn/posts/b58c3ba0.html</id>
    <published>2026-06-15T13:55:33.000Z</published>
    <updated>2026-06-15T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/503279b0.html">CentOS 7 通过 Shell 脚本实现磁盘监控报警</a></li><li><a href="/posts/b58c3ba0.html">Debian 13 通过 Shell 脚本实现磁盘监控报警</a></li></ul><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文将介绍 Debian 13 系统如何使用 Shell 脚本，实现对磁盘使用率的监控，并发送告警邮件（支持腾讯企业邮箱或者 QQ 邮箱）。</p><span id="more"></span><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="防火墙配置"><a href="#防火墙配置" class="headerlink" title="防火墙配置"></a>防火墙配置</h3><ul><li>安装 UFW 服务 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install ufw -y</span><br></pre></td></tr></tbody></table></figure><ul><li>防火墙开放 22 端口（<strong>避免 SSH 远程连接断开</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw allow 22/tcp</span><br></pre></td></tr></tbody></table></figure><ul><li>防火墙开放 465 端口（SMTPS，用于邮件发送）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw allow 465/tcp</span><br></pre></td></tr></tbody></table></figure><ul><li>启用防火墙（<strong>务必先开放 SSH 的 22 端口再启用防火墙</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw <span class="built_in">enable</span></span><br></pre></td></tr></tbody></table></figure><ul><li>查看防火墙运行状态和已开放端口 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw status verbose</span><br></pre></td></tr></tbody></table></figure><h3 id="安装邮件服务"><a href="#安装邮件服务" class="headerlink" title="安装邮件服务"></a>安装邮件服务</h3><ul><li>安装邮件服务 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 s-nail</span></span><br><span class="line">sudo apt install s-nail -y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建从 mail 到 s-nail 的软链接（可选操作，后续也可以直接使用 s-nail 命令）</span></span><br><span class="line">sudo ln -s /usr/bin/s-nail /usr/bin/mail</span><br></pre></td></tr></tbody></table></figure><h3 id="导入-NSS-证书"><a href="#导入-NSS-证书" class="headerlink" title="导入 NSS 证书"></a>导入 NSS 证书</h3><ul><li>安装 NSS 工具 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install libnss3-tools -y</span><br></pre></td></tr></tbody></table></figure><ul><li>导入 NSS 证书（<strong>这里使用 root 用户</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建用于存储证书的目录</span></span><br><span class="line">sudo mkdir -p /root/.certs/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从 QQ 邮箱的 SMTP 服务器（端口 465）获取 SSL 证书链，并提取出证书部分（BEGIN/END 标记之间），保存到 qq-smtp.crt 文件</span></span><br><span class="line">sudo sh -c <span class="string">"echo -n | openssl s_client -connect smtp.qq.com:465 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' &gt; /root/.certs/qq-smtp.crt"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将提取的证书中的 "GeoTrust SSL CA" 证书添加到 NSS 数据库（-d 指定目录），信任属性设置为 "C,,"（表示作为 CA 证书，但不用于客户端 / 服务器认证）</span></span><br><span class="line">sudo certutil -A -n <span class="string">"GeoTrust SSL CA"</span> -t <span class="string">"C,,"</span> -d /root/.certs -i /root/.certs/qq-smtp.crt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将提取的证书中的 "GeoTrust Global CA" 证书添加到 NSS 数据库，同样设置信任属性为 "C,,"</span></span><br><span class="line">sudo certutil -A -n <span class="string">"GeoTrust Global CA"</span> -t <span class="string">"C,,"</span> -d /root/.certs -i /root/.certs/qq-smtp.crt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出 NSS 数据库中所有已安装的证书，用于验证证书是否成功导入</span></span><br><span class="line">sudo certutil -L -d /root/.certs</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>(1) 在导入证书时，要特别注意当前使用的是哪个用户进行操作。</li><li>(2) 当使用 root 用户导入证书，那么在测试邮件发送、测试磁盘监控脚本、添加定时任务（比如 Crontab）的时候也必须使用 root 用户。</li><li>(3) 同理，当使用普通用户导入证书，那么在测试邮件发送、测试磁盘监控脚本、添加定时任务（比如 Crontab）的时候也必须使用普通用户。</li></ul></div><ul><li>获取腾讯邮箱授权密码 </li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">1. 获取授权码：</span><br><span class="line">   - 腾讯邮箱（包括腾讯企业邮箱和 QQ 邮箱）需要在邮箱设置中生成 SMTP 服务授权码。</span><br><span class="line">   - 登录邮箱，前往 设置 -&gt; 帐号设置 -&gt; SMTP 服务，生成授权码并使用它代替密码。</span><br><span class="line"></span><br><span class="line">2. 安全性设置：</span><br><span class="line">   - 确保已启用 SMTP 服务和客户端登录支持。</span><br><span class="line"></span><br><span class="line">3. SMTP 服务器地址：</span><br><span class="line">   - QQ 邮箱： smtp.qq.com</span><br><span class="line">   - 腾讯企业邮箱：smtp.exmail.qq.com</span><br></pre></td></tr></tbody></table></figure><ul><li>编辑系统配置文件，在文件末尾追加以下内容（<strong>请自行更改以下配置信息，切忌直接复制，这里使用的是 QQ 邮箱</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 备份配置文件</span></span><br><span class="line">sudo cp /etc/s-nail.rc /etc/s-nail.rc.bak</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编辑配置文件</span></span><br><span class="line">sudo vi /etc/s-nail.rc</span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> from=clay@qq.com</span><br><span class="line"><span class="built_in">set</span> smtp=<span class="string">"smtps://smtp.qq.com:465"</span></span><br><span class="line"><span class="built_in">set</span> smtp-auth-user=clay@qq.com</span><br><span class="line"><span class="built_in">set</span> smtp-auth-password=xxxxxxxxxxxx</span><br><span class="line"><span class="built_in">set</span> smtp-auth=login</span><br><span class="line"><span class="built_in">set</span> ssl-verify=ignore</span><br><span class="line"><span class="built_in">set</span> nss-config-dir=/root/.certs</span><br></pre></td></tr></tbody></table></figure><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>set from</code></td><td>设置发件人的邮箱地址，邮件的 “From” 字段会显示此地址。</td></tr><tr><td><code>set smtp</code></td><td>指定使用腾讯邮箱的 SMTP 服务器地址和端口号（465 为 SMTPS 加密端口）。若使用的是腾讯企业邮箱，那么应该配置为 <code>set smtp="smtps://smtp.exmail.qq.com:465"</code>。</td></tr><tr><td><code>set smtp-auth-user</code></td><td>指定用于 SMTP 身份验证的用户名，通常是发件人的完整邮箱地址。</td></tr><tr><td><code>set smtp-auth-password</code></td><td>指定用于 SMTP 身份验证的密码或授权码（QQ 邮箱需使用专用授权码）。</td></tr><tr><td><code>set smtp-auth</code></td><td>设置 SMTP 身份验证的模式为 <code>login</code>，常见的认证方式之一。</td></tr><tr><td><code>set ssl-verify</code></td><td>忽略 SSL 证书验证（适用于证书问题或调试环境，但不建议在生产环境中忽略验证）。</td></tr><tr><td><code>set nss-config-dir</code></td><td>指定 NSS（Network Security Services）库的证书配置目录，用于管理 SSL/TLS 证书和加密连接。</td></tr></tbody></table><h3 id="测试邮件发送"><a href="#测试邮件发送" class="headerlink" title="测试邮件发送"></a>测试邮件发送</h3><ul><li>测试邮件发送（<strong>这里使用 root 用户</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo sh -c <span class="string">"echo 'TestMail~' | mail -s 'TestMail' xxx@qq.com 2&gt;/dev/null"</span></span><br></pre></td></tr></tbody></table></figure><ul><li>如果邮件发送失败，可以通过添加 <code>-v</code> 参数输出详细的日志信息，然后进一步排查原因 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo sh -c <span class="string">"echo 'TestMail~' | mail -v -s 'TestMail' xxx@qq.com"</span></span><br></pre></td></tr></tbody></table></figure><h2 id="脚本实现"><a href="#脚本实现" class="headerlink" title="脚本实现"></a>脚本实现</h2><ul><li>创建磁盘监控脚本文件，并添加以下内容 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vi /opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 告警阈值</span></span><br><span class="line">THRESHOLD=90</span><br><span class="line"></span><br><span class="line"><span class="comment"># 邮件接收者（使用空格分割多个邮件接收者）</span></span><br><span class="line">EMAIL=<span class="string">"xxx@qq.com"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 磁盘设备</span></span><br><span class="line">DISK_DEVICE=/dev/sda1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 日志文件</span></span><br><span class="line">LOG_FILE=/tmp/disk_monitor.log</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建日志文件</span></span><br><span class="line"><span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$LOG_FILE</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">    touch <span class="string">"<span class="variable">$LOG_FILE</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取服务器的公网 IP</span></span><br><span class="line">IP_ADDRESS=$(curl -s https://checkip.amazonaws.com)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断服务器的公网 IP 是否为空</span></span><br><span class="line"><span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$IP_ADDRESS</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">   IP_ADDRESS=<span class="string">"0.0.0.0"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取指定磁盘设备的使用率</span></span><br><span class="line">CURRENT_USAGE=$(df -h <span class="variable">$DISK_DEVICE</span> | awk <span class="string">'NR==2 {print $5}'</span> | sed <span class="string">'s/%//g'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入日志文件</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="subst">$(date '+%Y-%m-%d %H:%M:%S')</span> 磁盘设备 <span class="variable">$DISK_DEVICE</span> 的使用率为 <span class="variable">$CURRENT_USAGE</span>%"</span> &gt;&gt; <span class="variable">$LOG_FILE</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断磁盘使用率是否超过阈值</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="string">"<span class="variable">$CURRENT_USAGE</span>"</span> -ge <span class="string">"<span class="variable">$THRESHOLD</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="comment"># 发送告警邮件</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"警告: 磁盘设备 <span class="variable">$DISK_DEVICE</span> 的空间使用率超过 <span class="variable">$CURRENT_USAGE</span>%, 请登录服务器 (<span class="variable">$IP_ADDRESS</span>) 及时清理文件."</span> | mail -s <span class="string">"磁盘空间告警"</span> <span class="variable">$EMAIL</span> 2&gt;/dev/null</span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></tbody></table></figure><ul><li>脚本文件授权 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod 755 /opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><ul><li>脚本测试执行（<strong>这里使用 root 用户</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo bash /opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">提示</p><ul><li>如果只希望监控根目录（<code>/</code>）的磁盘使用率，可以使用命令 <code>df -h / | awk 'NR==2 {print $5}' | sed 's/%//g'</code>。</li><li>如果有多个邮件接收者，可以将上述代码更改为 <code>EMAIL="example1@qq.com example2@qq.com</code>，多个邮箱地址之间使用空格分隔，<code>mail</code> 命令支持将邮件发送给 <code>EMAIL</code> 变量中的所有接收者。</li></ul></div><h2 id="定时检测"><a href="#定时检测" class="headerlink" title="定时检测"></a>定时检测</h2><div class="admonition note"><p class="admonition-title">提示</p><p>在 Debian 13 中，官方推荐使用 systemd 自带的 timer 功能取代传统的 cron 服务。当然，如果您更习惯 cron 的配置方式，也可以选择手动安装 cron 服务，然后使用 <code>crontab -e</code> 命令来管理定时任务。</p></div><ul><li>创建磁盘监控的服务单元 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建系统配置文件，添加以下配置信息</span></span><br><span class="line">sudo vi /etc/systemd/system/disk-monitor.service</span><br><span class="line"></span><br><span class="line">[Unit]</span><br><span class="line">Description=Disk Monitoring Service</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=oneshot</span><br><span class="line">ExecStart=/opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><ul><li>创建磁盘监控的定时器单元（比如每 30 分钟执行一次磁盘使用率检测）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建系统配置文件，添加以下配置信息</span></span><br><span class="line">sudo vi /etc/systemd/system/disk-monitor.timer</span><br><span class="line"></span><br><span class="line">[Unit]</span><br><span class="line">Description=Run disk monitor every 30 minutes</span><br><span class="line"></span><br><span class="line">[Timer]</span><br><span class="line">OnCalendar=*:0/30</span><br><span class="line">Persistent=<span class="literal">true</span></span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=timers.target</span><br></pre></td></tr></tbody></table></figure><ul><li>启动并启用磁盘监控的定时器 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重新加载系统配置文件</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开机启动并立刻启用磁盘监控的定时器</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> --now disk-monitor.timer</span><br></pre></td></tr></tbody></table></figure><ul><li>查看磁盘监控定时器的运行状态 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl list-timers --all</span><br></pre></td></tr></tbody></table></figure><h2 id="验证检测"><a href="#验证检测" class="headerlink" title="验证检测"></a>验证检测</h2><ul><li>查看磁盘监控脚本执行输出的日志信息 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看日志文件</span></span><br><span class="line">sudo tail -f -n 50 /tmp/disk_monitor.log</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2026-06-15 21:15:30 磁盘设备 /dev/sda1 的使用率为 85%</span><br></pre></td></tr></tbody></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.cnblogs.com/guowenrui/p/17220838.html">Linux 使用 mailx 发送腾讯邮件</a></li><li><a href="https://www.cnblogs.com/huangjiabobk/p/18302228">Linux Shell 脚本监控磁盘与邮件告警</a></li><li><a href="https://blog.csdn.net/WQZ_1998/article/details/108087371">Linux 使用 Shell 脚本监控磁盘与邮件告警</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍 Debian 13 如何使用 Shell 脚本实现磁盘空间监控报警。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="Debian" scheme="https://www.techgrow.cn/tags/Debian/"/>
    
    <category term="Shell 脚本编程" scheme="https://www.techgrow.cn/tags/Shell-%E8%84%9A%E6%9C%AC%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Debian 13 安装 Docker 与 Docker-Compose</title>
    <link href="https://www.techgrow.cn/posts/351cd7b1.html"/>
    <id>https://www.techgrow.cn/posts/351cd7b1.html</id>
    <published>2026-06-11T13:18:02.000Z</published>
    <updated>2026-06-11T13:18:02.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/efd9e9f2.html">CentOS 7 安装 Docker 与 Docker-Compose</a></li><li><a href="/posts/a4847c57.html">Debian 11 安装 Docker 与 Docker-Compose</a></li><li><a href="/posts/a19ep68d.html">Debian 12 安装 Docker 与 Docker-Compose</a></li><li><a href="/posts/351cd7b1.html">Debian 13 安装 Docker 与 Docker-Compose</a></li></ul><h2 id="官方资源"><a href="#官方资源" class="headerlink" title="官方资源"></a>官方资源</h2><ul><li><a href="https://www.docker.com/">Docker 官方网站</a></li><li><a href="https://docs.docker.com/">Docker 官方文档</a></li><li><a href="https://hub.docker.com/">Docker Hub 官方网站</a></li></ul><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="卸载已有软件"><a href="#卸载已有软件" class="headerlink" title="卸载已有软件"></a>卸载已有软件</h3><p>卸载已安装的 Docker，避免对后续的安装步骤产生影响。</p><span id="more"></span><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 卸载旧版本，包括有关的配置文件</span></span><br><span class="line">sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保证卸载干净</span></span><br><span class="line">sudo apt-get remove docker docker-engine docker.io containerd runc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除文件</span></span><br><span class="line">sudo rm -rf /etc/docker</span><br><span class="line">sudo rm -rf /var/lib/docker</span><br><span class="line">sudo rm -rf /usr/libexec/docker</span><br><span class="line">sudo rm -rf /var/lib/containerd</span><br><span class="line">sudo rm -rf /etc/apt/keyrings</span><br></pre></td></tr></tbody></table></figure><h3 id="安装依赖软件"><a href="#安装依赖软件" class="headerlink" title="安装依赖软件"></a>安装依赖软件</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release</span><br></pre></td></tr></tbody></table></figure><h2 id="安装-Docker"><a href="#安装-Docker" class="headerlink" title="安装 Docker"></a>安装 Docker</h2><h3 id="添加存储库"><a href="#添加存储库" class="headerlink" title="添加存储库"></a>添加存储库</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加 Docker 的官方 GPG 密钥</span></span><br><span class="line">sudo curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加 Docker 的 APT 存储库</span></span><br><span class="line">sudo <span class="built_in">echo</span> <span class="string">"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian <span class="subst">$(lsb_release -cs)</span> stable"</span> | tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新 APT 索引</span></span><br><span class="line">sudo apt update</span><br></pre></td></tr></tbody></table></figure><h3 id="Docker-安装"><a href="#Docker-安装" class="headerlink" title="Docker 安装"></a>Docker 安装</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装最新版本的 Docker</span></span><br><span class="line">sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">提示</p><p>Docker Buildx 是 Docker 官方推出的一款增强型构建工具，旨在简化和优化多架构容器映像的构建流程。它整合了 QEMU 模拟器和多平台构建能力，使得开发者可以在单一的构建过程中同时为多个平台（如 x86、ARM 等）构建容器映像，极大地提高了构建效率和开发体验。</p></div><h3 id="Docker-启动"><a href="#Docker-启动" class="headerlink" title="Docker 启动"></a>Docker 启动</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开机自启动 Docker</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动 Docker</span></span><br><span class="line">sudo systemctl start docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Docker 的运行状态</span></span><br><span class="line">sudo systemctl status docker</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">普通用户使用 Docker 命令</p><p>如果希望让非 <code>root</code> 用户也能使用 Docker 命令，可以将该用户添加到 <code>docker</code> 用户组，比如 <code>sudo usermod -aG docker your-user-name</code>。</p></div><h3 id="Docker-配置源"><a href="#Docker-配置源" class="headerlink" title="Docker 配置源"></a>Docker 配置源</h3><p>由于 Docker Hub 存在不可抗拒因素，导致在国内无法访问，因此需要配置 Docker 使用国内镜像源。</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建配置目录</span></span><br><span class="line">sudo mkdir -p /etc/docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建配置文件</span></span><br><span class="line">sudo tee /etc/docker/daemon.json &lt;&lt;-<span class="string">'EOF'</span></span><br><span class="line">{</span><br><span class="line">  <span class="string">"registry-mirrors"</span>: [</span><br><span class="line">     <span class="string">"https://ustc-edu-cn.mirror.aliyuncs.com"</span>,</span><br><span class="line">     <span class="string">"https://mirror.iscas.ac.cn"</span>,</span><br><span class="line">     <span class="string">"https://docker.nju.edu.cn"</span>,</span><br><span class="line">     <span class="string">"https://docker.m.daocloud.io"</span>,</span><br><span class="line">     <span class="string">"https://ccr.ccs.tencentyun.com"</span>,</span><br><span class="line">     <span class="string">"https://dockerhub.timeweb.cloud"</span></span><br><span class="line">    ]</span><br><span class="line">}</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使配置文件生效</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启 Docker</span></span><br><span class="line">sudo systemctl restart docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Docker 的运行状态</span></span><br><span class="line">sudo systemctl status docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Docker 的运行信息</span></span><br><span class="line">sudo docker info</span><br></pre></td></tr></tbody></table></figure><h3 id="Docker-验证安裝"><a href="#Docker-验证安裝" class="headerlink" title="Docker 验证安裝"></a>Docker 验证安裝</h3><p>验证是否正确安装了 Docker，以下命令将下载一个测试镜像并在容器中运行它，容器在运行时将输出一条参考消息并退出</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo docker run hello-world</span><br></pre></td></tr></tbody></table></figure><h2 id="安装-Docker-Compose"><a href="#安装-Docker-Compose" class="headerlink" title="安装 Docker-Compose"></a>安装 Docker-Compose</h2><div class="admonition note"><p class="admonition-title">提示</p><p>推荐使用第二种方式（基于 <code>curl</code> 命令）进行安装，因为可以指定具体安装哪个版本的 Docker-Compose，而且后期升级维护也更方便。</p></div><h3 id="第一种安装方式"><a href="#第一种安装方式" class="headerlink" title="第一种安装方式"></a>第一种安装方式</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 Docker-Compose 插件</span></span><br><span class="line">sudo apt install -y docker-compose-plugin</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证 Docker-Compose 插件</span></span><br><span class="line">sudo docker compose version</span><br></pre></td></tr></tbody></table></figure><h3 id="第二种安装方式"><a href="#第二种安装方式" class="headerlink" title="第二种安装方式"></a>第二种安装方式</h3><p>使用 <code>curl</code> 命令来下载 Docker-Compose 的二进制包，除此之外还可以在 <a href="https://github.com/docker/compose/releases">GitHub Releases</a> 上面手动下载对应的文件，然后上传到目标服务器。</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载文件</span></span><br><span class="line">sudo curl -L <span class="string">"https://github.com/docker/compose/releases/download/1.29.1/docker-compose-<span class="subst">$(uname -s)</span>-<span class="subst">$(uname -m)</span>"</span> -o /usr/<span class="built_in">local</span>/bin/docker-compose</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件授权</span></span><br><span class="line">sudo chmod +x /usr/<span class="built_in">local</span>/bin/docker-compose</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证下载</span></span><br><span class="line">sudo docker-compose -v</span><br></pre></td></tr></tbody></table></figure><h2 id="常见安装问题"><a href="#常见安装问题" class="headerlink" title="常见安装问题"></a>常见安装问题</h2><h3 id="info-命令输出警告信息"><a href="#info-命令输出警告信息" class="headerlink" title="info 命令输出警告信息"></a>info 命令输出警告信息</h3><ul><li>问题描述：当 Docker 安装完成之后，执行 <code>docker info</code> 命令，发现有以下警告信息存在：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">WARNING: No cpu cfs quota support</span><br><span class="line">WARNING: No cpu cfs period support</span><br><span class="line">WARNING: No cpu shares support</span><br><span class="line">WARNING: No cpuset support</span><br><span class="line">WARNING: No io.weight support</span><br><span class="line">WARNING: No io.weight (per device) support</span><br><span class="line">WARNING: No io.max (rbps) support</span><br><span class="line">WARNING: No io.max (wbps) support</span><br><span class="line">WARNING: No io.max (riops) support</span><br><span class="line">WARNING: No io.max (wiops) support</span><br></pre></td></tr></tbody></table></figure><ul><li>解决办法：重启 Debian 服务器，让系统重新加载 <code>cgroup</code> 规则</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="/posts/57bca56f.html">Docker 介绍与安装</a></li><li><a href="/posts/b31f4d18.html">Docker-Compose 安装与使用</a></li><li><a href="https://blog.csdn.net/weixin_49187218/article/details/136935982">Debian 11 上安装 Docker</a></li><li><a href="https://blog.csdn.net/qq_30818545/article/details/124514016">Debian11 之 Docker 稳定版本安装</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍 Debian 13 如何安装 Docker 与 Docker-Compose。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="容器化" scheme="https://www.techgrow.cn/tags/%E5%AE%B9%E5%99%A8%E5%8C%96/"/>
    
    <category term="Debian" scheme="https://www.techgrow.cn/tags/Debian/"/>
    
  </entry>
  
  <entry>
    <title>Debian 13 生产环境安装 Nginx</title>
    <link href="https://www.techgrow.cn/posts/de53727a.html"/>
    <id>https://www.techgrow.cn/posts/de53727a.html</id>
    <published>2026-05-27T11:50:21.000Z</published>
    <updated>2026-05-27T11:50:21.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><h3 id="系统环境"><a href="#系统环境" class="headerlink" title="系统环境"></a>系统环境</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Linux option 6.12.90+deb13-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.90-1 (2026-05-22) x86_64 GNU/Linux</span><br></pre></td></tr></tbody></table></figure><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="安装-Supervisor"><a href="#安装-Supervisor" class="headerlink" title="安装 Supervisor"></a>安装 Supervisor</h3><p>Supervisor 主要用于管理 Nginx 的开机自启动（带守护进程）。</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装</span></span><br><span class="line"><span class="keyword"># yum</span> install<span class="params"> -y</span> supervisor</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开机自启动</span></span><br><span class="line"><span class="keyword"># systemctl</span> <span class="built_in">enable</span> supervisord</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动服务</span></span><br><span class="line"><span class="keyword"># systemctl</span> start supervisord</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看服务状态</span></span><br><span class="line"><span class="keyword"># systemctl</span> status supervisord</span><br></pre></td></tr></tbody></table></figure><span id="more"></span><h3 id="创建-Nginx-用户和用户组"><a href="#创建-Nginx-用户和用户组" class="headerlink" title="创建 Nginx 用户和用户组"></a>创建 Nginx 用户和用户组</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 切换root用户</span></span><br><span class="line"><span class="keyword">$ sudo</span><span class="params"> -i</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建nginx用户组</span></span><br><span class="line"><span class="keyword"># groupadd</span> nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建nginx用户（不允许远程登录）</span></span><br><span class="line"><span class="keyword"># useradd</span><span class="params"> -g</span> nginx nginx<span class="params"> -s</span> /bin/<span class="literal">false</span></span><br></pre></td></tr></tbody></table></figure><h3 id="更改最大打开文件描述符数"><a href="#更改最大打开文件描述符数" class="headerlink" title="更改最大打开文件描述符数"></a>更改最大打开文件描述符数</h3><ul><li><a href="/posts/88a10b.html">Centos 7 更改最大文件描述符数</a></li><li><a href="/posts/a29573a6.html">Debian 12 更改最大文件描述符数</a></li></ul><h2 id="Nginx-编译安装"><a href="#Nginx-编译安装" class="headerlink" title="Nginx 编译安装"></a>Nginx 编译安装</h2><h3 id="下载文件"><a href="#下载文件" class="headerlink" title="下载文件"></a>下载文件</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建下载目录</span></span><br><span class="line"><span class="keyword"># mkdir</span><span class="params"> -p</span> /home/nginx/software</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载</span></span><br><span class="line"><span class="keyword"># cd</span> /home/nginx/software</span><br><span class="line"><span class="keyword"># wget</span> http://nginx.org/download/nginx-1.26.3.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line"><span class="keyword"># tar</span><span class="params"> -xvf</span> nginx-1.26.3.tar.gz</span><br></pre></td></tr></tbody></table></figure><h3 id="编译安装"><a href="#编译安装" class="headerlink" title="编译安装"></a>编译安装</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入下载目录</span></span><br><span class="line"><span class="keyword"># cd</span> /home/nginx/software/nginx-1.26.3</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装依赖库</span></span><br><span class="line"><span class="keyword"># apt</span>-get install build-essential libpcre2-dev zlib1g zlib1g-dev openssl libssl-dev</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置</span></span><br><span class="line">./configure \<span class="params"></span></span><br><span class="line"><span class="params">  --user</span>=nginx \<span class="params"></span></span><br><span class="line"><span class="params">  --group</span>=nginx \<span class="params"></span></span><br><span class="line"><span class="params">  --prefix</span>=/usr/<span class="built_in">local</span>/nginx \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-pcre \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_v2_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_ssl_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_realip_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_gzip_static_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_stub_status_module</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译安装</span></span><br><span class="line"><span class="keyword"># make</span> &amp;&amp; make install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 备份默认的配置文件</span></span><br><span class="line"><span class="keyword"># cd</span> /usr/<span class="built_in">local</span>/nginx/conf</span><br><span class="line"><span class="keyword"># cp</span> nginx.conf nginx.conf.default</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件授权</span></span><br><span class="line"><span class="keyword"># chown</span><span class="params"> -R</span> nginx:nginx /usr/<span class="built_in">local</span>/nginx</span><br></pre></td></tr></tbody></table></figure><h3 id="配置-Nginx"><a href="#配置-Nginx" class="headerlink" title="配置 Nginx"></a>配置 Nginx</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑nginx的配置文件</span></span><br><span class="line"><span class="keyword"># vim</span> /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf</span><br><span class="line">worker_processes  4;</span><br><span class="line">error_log  logs/error.<span class="built_in">log</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 校验配置文件是否正确</span></span><br><span class="line"><span class="comment"># /usr/local/nginx/sbin/nginx -t</span></span><br></pre></td></tr></tbody></table></figure><h3 id="配置防火墙"><a href="#配置防火墙" class="headerlink" title="配置防火墙"></a>配置防火墙</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动防火墙</span></span><br><span class="line"><span class="keyword"># ufw</span> <span class="built_in">enable</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 开放端口</span></span><br><span class="line"><span class="keyword"># ufw</span> allow 80/tcp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看已开放的端口</span></span><br><span class="line"><span class="keyword"># ufw</span> status verbose</span><br></pre></td></tr></tbody></table></figure><h2 id="Nginx-服务管理"><a href="#Nginx-服务管理" class="headerlink" title="Nginx 服务管理"></a>Nginx 服务管理</h2><h3 id="开机自启动-Nginx"><a href="#开机自启动-Nginx" class="headerlink" title="开机自启动 Nginx"></a>开机自启动 Nginx</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建nginx的supervistor配置文件</span></span><br><span class="line"><span class="keyword"># touch</span> /etc/supervisor/conf.d/nginx.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编辑nginx的supervistor配置文件</span></span><br><span class="line"><span class="keyword"># vim</span> /etc/supervisor/conf.d/nginx.conf</span><br><span class="line">[program:nginx]</span><br><span class="line">directory=/usr/<span class="built_in">local</span>/nginx</span><br><span class="line"><span class="built_in">command</span>=/usr/<span class="built_in">local</span>/nginx/sbin/nginx<span class="params"> -g</span> <span class="string">'daemon off;'</span><span class="params"> -c</span> /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf</span><br><span class="line">user=root</span><br><span class="line">numprocs=1</span><br><span class="line">autostart=<span class="literal">true</span></span><br><span class="line">autorestart=<span class="literal">true</span></span><br><span class="line">startretries=10</span><br><span class="line">process_name=%(program_name)s</span><br><span class="line">stdout_logfile_backups=5</span><br><span class="line">stdout_logfile_maxbytes=10MB</span><br><span class="line">stdout_logfile=/var/<span class="built_in">log</span>/supervisor/nginx.<span class="built_in">log</span></span><br><span class="line">stderr_logfile_backups=5</span><br><span class="line">stderr_logfile_maxbytes=10MB</span><br><span class="line">stderr_logfile=/var/<span class="built_in">log</span>/supervisor/nginx-error.<span class="built_in">log</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 上面的配置，主进程会以root用户运行，worker进程会以nginx用户运行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重载nginx的supervistor配置文件，会自动启动nginx服务</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看nginx的运行状态</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> status nginx</span><br><span class="line">nginx RUNNING   pid 9451, uptime 0:00:56     <span class="comment">#如果输出此日志信息，说明nginx启动成功，否则查看nginx的启动日志来排查问题</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试访问nginx</span></span><br><span class="line"><span class="keyword"># curl</span><span class="params"> -I</span><span class="params"> -X</span> GET 127.0.0.1:80</span><br></pre></td></tr></tbody></table></figure><h3 id="管理-Nginx-服务"><a href="#管理-Nginx-服务" class="headerlink" title="管理 Nginx 服务"></a>管理 Nginx 服务</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 关闭</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> stop nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> start nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> restart nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> status nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 平滑更新nginx的配置文件</span></span><br><span class="line"><span class="comment"># /usr/local/nginx/sbin/nginx -s reload</span></span><br></pre></td></tr></tbody></table></figure><h2 id="Nginx-配置文件概述"><a href="#Nginx-配置文件概述" class="headerlink" title="Nginx 配置文件概述"></a>Nginx 配置文件概述</h2><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">安装目录：/usr/local/nginx</span><br><span class="line">配置文件：/usr/local/nginx/conf/nginx.conf</span><br><span class="line">错误日志：/usr/local/nginx/logs/error.log</span><br><span class="line">访问日志：/usr/local/nginx/logs/access.log</span><br><span class="line">nginx的supervistor配置文件：/etc/supervisor/conf.d/nginx.conf</span><br></pre></td></tr></tbody></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://juejin.cn/post/7061998261904605214">详解 Debian 系统上如何编译安装 Nginx</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍 Debian 13 生产环境如何编译安装 Nginx。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="Web服务器" scheme="https://www.techgrow.cn/tags/Web%E6%9C%8D%E5%8A%A1%E5%99%A8/"/>
    
    <category term="Debian" scheme="https://www.techgrow.cn/tags/Debian/"/>
    
  </entry>
  
  <entry>
    <title>磁盘阵列（RAID）技术介绍</title>
    <link href="https://www.techgrow.cn/posts/44a8c7ec.html"/>
    <id>https://www.techgrow.cn/posts/44a8c7ec.html</id>
    <published>2026-04-05T14:08:21.000Z</published>
    <updated>2026-04-05T14:08:21.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="RAID-历史"><a href="#RAID-历史" class="headerlink" title="RAID 历史"></a>RAID 历史</h2><p>1988 年美国加州大学伯克利分校的 D.A.Patterson 教授等首次在论文 “A Case of Redundant Array of Inexpensive Disks” 中提出了 RAID 概念，即廉价冗余磁盘阵列（ Redundant Array of Inexpensive Disks）。由于当时大容量磁盘比较昂贵，RAID 的基本思想是将多个容量较小、相对廉价的磁盘进行有机组合，从而以较低的成本获得与昂贵大容量磁盘相当的容量、性能、可靠性。随着磁盘成本和价格的不断降低，” 廉价” 已经变得毫无意义。因此，RAID 咨询委员会（RAID Advisory Board，RAB）决定用 “独立” 替代 “廉价”，于是 RAID 变成了独立磁盘冗余阵列（Redundant Array of Independent Disks）。但这仅仅是名称的变化，实质内容并没有改变。</p><span id="more"></span><h2 id="RAID-等级"><a href="#RAID-等级" class="headerlink" title="RAID 等级"></a>RAID 等级</h2><ul><li><p>RAID 这种设计思想很快被业界接纳，RAID 技术作为高性能、高可靠的存储技术，得到了非常广泛的应用。RAID 主要利用镜像、数据条带和数据校验三种技术来获取高性能、可靠性、容错能力和扩展性，根据对这三种技术的使用策略和组合架构，可以把 RAID 分为不同的等级，以满足不同数据应用的需求。</p></li><li><p>D.A.Patterson 教授等的论文中定义了 RAID0 ~ RAID6 原始 RAID 等级。随后存储厂商又不断推出 RAID7、RAID10、RAID01、RAID50、RAID53、RAID100 等 RAID 等级，但这些并无统一的标准。<strong>目前业界与学术界公认的标准是 RAID0 ~ RAID6，而在实际应用领域中使用最多的 RAID 等级是 RAID0、RAID1、RAID3、RAID5、RAID6 和 RAID10</strong>。</p></li><li><p>RAID 每一个等级代表一种实现方法和技术，等级之间并无高低之分。在实际应用中，应当根据用户的数据特点，综合考虑可用性、性能和成本来选择合适的 RAID 等级，以及具体的实现方式。</p></li></ul><div class="admonition note"><p class="admonition-title">RAID 等级的读法</p><p>RAID0、RAID1、RAID10、RAID100 等通常按数字逐位读出，例如 RAID10 读作 "RAID 一零"，而不是 "RAID 十"。</p></div><h2 id="RAID-分类"><a href="#RAID-分类" class="headerlink" title="RAID 分类"></a>RAID 分类</h2><p>从实现角度来看，RAID 主要分为软 RAID、硬 RAID 以及混合 RAID 三种。</p><h3 id="软-RAID"><a href="#软-RAID" class="headerlink" title="软 RAID"></a>软 RAID</h3><p>所有功能都是由操作系统和 CPU 来完成，没有独立的 RAID 控制处理芯片和 I/O 处理芯片，性能是最低的，但成本也是最低。</p><h3 id="硬-RAID"><a href="#硬-RAID" class="headerlink" title="硬 RAID"></a>硬 RAID</h3><p>配备了专门的 RAID 控制处理芯片和 I/O 处理芯片以及阵列缓冲，不占用 CPU 资源。性能是最高的，但成本也是最高。</p><h3 id="混合-RAID"><a href="#混合-RAID" class="headerlink" title="混合 RAID"></a>混合 RAID</h3><p>具备 RAID 控制处理芯片，但没有专门的 I/O 处理芯片，需要 CPU 和驱动程序来处理 I/O。性能和成本介于软 RAID 和硬 RAID 之间。</p><h2 id="RAID-关键技术"><a href="#RAID-关键技术" class="headerlink" title="RAID 关键技术"></a>RAID 关键技术</h2><h3 id="镜像技术"><a href="#镜像技术" class="headerlink" title="镜像技术"></a>镜像技术</h3><ul><li><p>镜像技术是一种冗余技术，为磁盘提供数据备份功能，防止磁盘发生故障而造成数据丢失。对于 RAID 而言，镜像技术最典型地的用法就是，同时在磁盘阵列中产生两个完全相同的数据副本，并且分布在两个不同的磁盘上。</p></li><li><p>镜像技术提供了完全的数据冗余能力，当一个数据副本失效不可用时，外部系统仍可正常访问另一副本，不会对应用系统运行和性能产生影响。而且，镜像不需要额外的计算和校验，故障修复非常快，直接复制即可。</p></li><li><p><strong>镜像技术支持从多个副本并发读取以提升读性能；写入时会同时写入所有副本（支持并行写），但由于需要等待全部磁盘写入完成，写性能通常不会提升，反而可能略有下降，最终写入速度取决于写入最慢的那块磁盘。</strong></p></li><li><p>镜像技术提供了非常高的数据安全性，其代价也是非常昂贵的，需要至少双倍的存储空间。高成本限制了镜像的广泛应用，主要应用于至关重要的数据保护，这种场合下的数据丢失可能会造成非常巨大的损失。</p></li></ul><h3 id="数据条带化技术"><a href="#数据条带化技术" class="headerlink" title="数据条带化技术"></a>数据条带化技术</h3><ul><li>数据条带化技术是一种自动将 I/O 操作负载均衡到多个物理磁盘上的技术。更具体地说就是，将一块连续的数据分成很多小部分并把它们分别存储到不同磁盘上。这就能使多个进程可以并发访问数据的多个不同部分，从而获得最大程度上的 I/O 并行能力，极大地提升性能。</li></ul><h3 id="数据校验技术"><a href="#数据校验技术" class="headerlink" title="数据校验技术"></a>数据校验技术</h3><ul><li><p>数据校验技术是指 RAID 在写入数据的同时进行校验计算，并将得到的校验数据存储在 RAID 成员磁盘中。校验数据可以集中保存在某个磁盘或分散存储在多个不同磁盘中。当其中一部分数据出错时，就可以对剩余数据和校验数据进行反校验计算重建丢失的数据。</p></li><li><p>数据校验技术相对于镜像技术的优势在于节省大量存储开销，但由于每次数据读写都要进行大量的校验运算，对计算机的 CPU 运算速度要求很高，<strong>且必须使用硬件 RAID 控制器</strong>。在数据重建恢复方面，检验技术比镜像技术复杂得多且慢得多。</p></li></ul><h2 id="RAID-常见等级详解"><a href="#RAID-常见等级详解" class="headerlink" title="RAID 常见等级详解"></a>RAID 常见等级详解</h2><h3 id="JBOD"><a href="#JBOD" class="headerlink" title="JBOD"></a>JBOD</h3><p><img data-src="../../../asset/2026/04/disk-raid-1.png"></p><ul><li><strong>JBOD（Just a Bunch of Disks，磁盘簇），表示一个没有控制软件提供协调控制的磁盘集合，这是 RAID 区别与 JBOD 的主要因素。</strong>JBOD 将多个物理磁盘串联起来，提供一个巨大的逻辑磁盘。</li><li>JBOD 的数据存放机制是由第一块磁盘开始按顺序往后存储，当前磁盘的存储空间用完后，再依次往后面的磁盘存储数据。JBOD 存储性能完全等同于单块磁盘，而且也不提供数据安全保护。</li><li>JBOD 本质上只是简单提供一种扩展存储空间的机制，JBOD 可用存储容量等于所有成员磁盘的存储空间之和。</li><li>JBOD 通常是指磁盘柜，而不论其是否提供 RAID 功能。不过，JBOD 并非官方术语，官方称为 <code>Spanning</code>。</li></ul><h3 id="RAID0"><a href="#RAID0" class="headerlink" title="RAID0"></a>RAID0</h3><p><img data-src="../../../asset/2026/04/disk-raid-2.png"></p><ul><li><p><strong>RAID0 是一种简单的、无数据校验的数据条带化技术。</strong>实际上不是一种真正的 RAID，因为它并不提供任何形式的冗余策略。RAID0 将所在磁盘条带化后组成大容量的存储空间，将数据分散存储在所有磁盘中，以独立访问方式实现多块磁盘的并读访问。</p></li><li><p><strong>一个由 N 块磁盘组成的 RAID0，它的理论读写性能是单个磁盘性能的 N 倍</strong>，但由于总线带宽等多种因素的限制，实际的性能提升低于理论值。</p></li><li><p>由于 RAID0 可以并发执行 I/O 操作，总线带宽得到充分利用，并且不需要进行数据校验，因此 <strong>RAID0 的性能在所有 RAID 等级中是最高的</strong>。</p></li><li><p>RAID0 具有低成本、高读写性能、100% 的高存储空间利用率等优点，但是它不提供数据冗余保护，一旦磁盘损坏，将无法恢复数据。</p></li><li><p>RAID0 的适用场景：</p><ul><li>对数据的顺序读写速度要求不高，对数据的安全性和可靠性要求不高，但对系统性能要求很高的场景，比如：</li><li>视频剪辑 / 渲染缓存（临时素材处理，追求高读写速度）</li><li>游戏加载盘（加快加载速度，对数据丢失不敏感）</li><li>缓存服务器（如临时缓存、CDN 缓存数据）</li><li>大数据 / 科学计算临时数据盘（中间结果可丢失）</li></ul></li><li><p>RAID0 与 JBOD 相同点：</p><ul><li>存储容量：都是成员磁盘容量总和</li><li>磁盘利用率都是 100%，即都没有做任何的数据冗余备份</li></ul></li><li><p> RAID0 与 JBOD 不同点：</p><ul><li>JBOD：数据是顺序存放的，一个磁盘存满后才会开始存放到下一个磁盘</li><li> RAID0：通过数据条带技术将数据分布到多个磁盘，实现并行读写，在理想情况下其读写性能可接近单盘的 N 倍（N 为磁盘数量），但实际性能会受到硬件和访问模式等因素影响</li></ul></li></ul><h3 id="RAID1"><a href="#RAID1" class="headerlink" title="RAID1"></a>RAID1</h3><p><img data-src="../../../asset/2026/04/disk-raid-3.png"></p><ul><li><p><strong>RAID1 是一种镜像技术，它将数据完全一致地分别写到工作磁盘和镜像磁盘，它的磁盘空间利用率为 50%。</strong></p></li><li><p><strong>RAID1 支持从多个副本并发读取以提升读性能，写入时会同时写入所有副本（支持并行写），但由于需要等待全部磁盘写入完成，写性能通常不会提升，反而可能略有下降</strong>。</p></li><li><p>RAID1 提供了最佳的数据保护，一旦工作磁盘发生故障，系统将自动切换到镜像磁盘，不会影响使用。</p></li><li><p>RAID1 是为了增强数据安全性使两块磁盘数据呈现完全镜像，从而达到安全性好、技术简单、管理方便。RAID1 拥有完全容错的能力，但实现成本高。</p></li><li><p>RAID1 的适用场景：</p><ul><li>对顺序读写性能要求较高，或对数据安全性要求较高的场景，比如：</li><li>数据库服务器（如订单、用户数据，要求高可靠性）</li><li>金融交易系统（不允许丢失数据）</li><li>企业文件服务器 / NAS（重要文件存储）</li><li>操作系统盘（保证系统不因磁盘损坏而宕机）</li></ul></li></ul><h3 id="RAID10"><a href="#RAID10" class="headerlink" title="RAID10"></a>RAID10</h3><p><img data-src="../../../asset/2026/04/disk-raid-4.png"></p><ul><li><p>RAID10 是一个 RAID1 与 RAID0 的组合体，所以它继承了 RAID1 的高安全性和 RAID0 的高性能。</p></li><li><p>RAID10 是先进行数据镜像（RAID1），再在多个镜像组之间进行数据条带化（RAID0），即先保证数据冗余，再提升读写性能。</p></li></ul><div class="admonition warning"><p class="admonition-title">RAID10 与 RAID01 的核心区别</p><ul><li>RAID10：先冗余（镜像），再提速（条带化）</li><li>RAID01：先提速（条带化），再冗余（镜像），可靠性不如 RAID10</li></ul></div><h3 id="RAID01"><a href="#RAID01" class="headerlink" title="RAID01"></a>RAID01</h3><p><img data-src="../../../asset/2026/04/disk-raid-5.png"></p><ul><li><p>RAID01 是一个 RAID0 与 RAID1 的组合体，所以它继承了 RAID0 的高性能和 RAID1 的高安全性。</p></li><li><p>RAID01 是先进行数据条带化（RAID0），再对条带后的数据整体做镜像（RAID1），即先提升读写性能，再保证数据冗余。</p></li><li><p>RAID01 的可靠性低于 RAID10，一旦某个条带组中的一块磁盘损坏，整个条带组都会失效，从而使磁盘阵列退化为单一镜像组，容错能力较弱。</p></li></ul><div class="admonition warning"><p class="admonition-title">RAID10 与 RAID01 的选择建议</p><p>RAID10 的容错率（可靠性）要比 RAID01 高，所以生产环境中一般是不使用 RAID01，而是使用 RAID10。</p></div><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://cloud.tencent.com/developer/article/2046751">RAID 技术全解图解</a></li><li><a href="https://zhuanlan.zhihu.com/p/51170719">RAID 磁盘阵列是什么</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍磁盘阵列技术，包括 RAID0、RAID1、RAID10、RAID01 等。</summary>
    
    
    
    
    <category term="Linux运维" scheme="https://www.techgrow.cn/tags/Linux%E8%BF%90%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>Linux 网络编程随笔</title>
    <link href="https://www.techgrow.cn/posts/a3872b61.html"/>
    <id>https://www.techgrow.cn/posts/a3872b61.html</id>
    <published>2026-03-23T13:55:33.000Z</published>
    <updated>2026-03-23T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="TCP-连接，一端断电和进程崩溃有什么区别"><a href="#TCP-连接，一端断电和进程崩溃有什么区别" class="headerlink" title="TCP 连接，一端断电和进程崩溃有什么区别"></a>TCP 连接，一端断电和进程崩溃有什么区别</h2><span id="more"></span><blockquote><p>问题剖析</p></blockquote><p>TCP 连接，没有打开 Keepalive 选项，没有数据交互，现在一端突然断电或者一端的进程崩溃了，这两种情况有什么区别呢？这是腾讯的一道面试题，其中有几个关键词：</p><ul><li>TCP 连接没有开启 Keepalive；</li><li>一直没有数据交互；</li><li>进程崩溃；</li><li>主机宕机；</li></ul><blockquote><p>什么是 TCP Keepalive</p></blockquote><p>TCP Keepalive 其实就是 TCP 的保活机制，它的工作原理如下：</p><p><img data-src="../../../asset/2026/03/tcp-keepalive-1.png"></p><p>如果两端的 TCP 连接一直没有数据交互，达到了触发 TCP 保活机制的条件，那么系统内核里的 TCP 协议栈就会发送探测报文。</p><ul><li>如果对端程序是正常工作的。当 TCP 保活的探测报文发送给对端，对端会正常响应，这样 <strong>TCP 保活时间会被重置</strong>，等待下一个 TCP 保活时间的到来。</li><li>如果对端主机宕机，或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后，石沉大海，没有响应，连续几次，达到保活探测次数后，<strong>TCP 会报告该 TCP 连接已经消亡</strong>。</li></ul><p>所以，TCP 保活机制可以在双方没有数据交互的情况，通过探测报文，来确定对方的 TCP 连接是否存活。<br>注意，应用程序若想使用 TCP 保活机制需要通过 Socket 接口设置 <code>SO_KEEPALIVE</code> 选项才能够生效，如果没有设置，那么就无法使用 TCP 保活机制。</p><blockquote><p>主机宕机</p></blockquote><p>在没有开启 TCP Keepalive，且双方一直没有数据交互的情况下，如果客户端的「主机宕机」了，会发生什么？</p><p>客户端主机宕机了，<strong>服务端是无法感知到的</strong>，再加上服务端没有开启 TCP Keepalive，又没有数据交互的情况下，<strong>服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态，直到服务端重启进程</strong>。</p><p>所以，这里可以得知一个点，在没有使用 TCP 保活机制且双方不传输数据的情况下，一方的 TCP 连接处在 <code>ESTABLISHED</code> 状态，并不代表另一方的连接还一定正常。</p><blockquote><p>进程崩溃</p></blockquote><p>在没有开启 TCP Keepalive，且双方一直没有数据交互的情况下，如果服务端的「进程崩溃」了，会发生什么？</p><p>TCP 的连接信息是由系统内核维护的，所以当服务端的进程崩溃后，系统内核需要回收该进程的所有 TCP 连接资源，于是系统内核会发送第一次挥手 FIN 报文，后续的挥手过程也都是在系统内核完成，并不需要进程参与，所以即使服务端的进程退出了，还是能与客户端完成 TCP 四次挥手的过程。</p><p>这里可以尝试做实验，使用 <code>kill -9</code> 来模拟服务端进程崩溃的情况，发现在 kill 掉进程后，服务端会发送 FIN 报文，与客户端进行四次挥手。</p><p>所以，即使没有开启 TCP Keepalive，且双方也没有数据交互的情况下，如果任意一方的进程发生了崩溃，这个过程系统内核是可以感知的到的，于是系统内核就会发送 FIN 报文给对方，然后与对方进行 TCP 四次挥手。</p><blockquote><p>有数据传输的场景</p></blockquote><p>以上就是对这道面试题的回答，接下来我们看看在「有数据传输」的场景下的一些异常情况：</p><p>第一种，客户端主机宕机，又迅速重启，会发生什么？<br>第二种，客户端主机宕机，一直没有重启，会发生什么？</p><hr><p>假设客户端主机宕机，又迅速重启，会发生什么？</p><p>在客户端主机宕机后，服务端向客户端发送的报文会得不到任何的响应，在一定时长后，服务端就会触发超时重传机制，重传未得到响应的报文给客户端。</p><p>在服务端重传报文的过程中，客户端主机重启完成后，客户端的内核就会接收重传的报文，然后根据报文的信息传递给对应的进程：</p><ul><li>如果客户端主机上没有进程绑定该 TCP 报文的目标端口号，那么客户端内核就会<strong>回复 RST 报文，重置该 TCP 连接</strong>；</li><li>如果客户端主机上有进程绑定该 TCP 报文的目标端口号，由于客户端主机重启后，之前的 TCP 连接的数据结构已经丢失了，客户端内核里协议栈会发现找不到该 TCP 连接的 Socket 结构体，于是就会<strong>回复 RST 报文，重置该 TCP 连接</strong>。</li></ul><p>所以，<strong>只要有任意一方的主机重启完成后，收到之前 TCP 连接的报文，都会回复 RST 报文给对端，以断开连接</strong>。</p><hr><p>假设客户端主机宕机，一直没有重启，会发生什么？</p><p>这种情况，服务端超时重传报文的次数达到一定阈值后，系统内核就会判定出该 TCP 有问题，然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了，于是服务端的 TCP 连接就会断开。</p><p>那 TCP 的数据报文具体重传几次呢？在 Linux 系统中，提供一个叫 <code>tcp_retries2</code> 配置项（<code>cat /proc/sys/net/ipv4/tcp_retries2</code>），默认值是 <code>15</code>；这个内核参数是控制在 TCP 连接建立的情况下，超时重传的最大次数。</p><p>不过 <code>tcp_retries2</code> 设置了 15 次，并不代表 TCP 超时重传了 15 次才会通知应用程序终止该 TCP 连接，内核会根据 <code>tcp_retries2</code> 设置的值，计算出一个 <code>timeout</code>（如果 <code>tcp_retries2 = 15</code>，那么计算得到的 <code>timeout = 924600ms</code>），如果重传间隔超过这个 <code>timeout</code>，则认为超过了阈值，就会停止重传，然后就会断开 TCP 连接。</p><p>在发生超时重传的过程中，每一轮的超时时间（RTO）都是倍数增长的，比如：如果第一轮 RTO 是 200 毫秒，那么第二轮 RTO 是 400 毫秒，第三轮 RTO 是 800 毫秒，以此类推。</p><p>而 RTO 是基于 RTT（一个包的往返时间）来计算的，如果 RTT 较大，那么计算出来的 RTO 就越大，那么经过几轮重传后，很快就达到了上面的 <code>timeout</code> 阀值了。</p><p>举个例子，如果 <code>tcp_retries2 = 15</code>，那么计算得到的 <code>timeout = 924600ms</code>，如果重传总间隔时长超过了 <code>924600ms</code> 就会停止重传，然后就会断开 TCP 连接：</p><ul><li>如果 RTT 比较小，那么 RTO 初始值就约等于下限 <code>200ms</code>，也就是第一轮的超时时间是 200 毫秒，由于 <code>timeout</code> 总时长是 <code>924600ms</code>，表现出来的现象刚好就是重传了 15 次，超过了 <code>timeout</code> 值，从而断开 TCP 连接</li><li>如果 RTT 比较大，假设 RTO 初始值计算得到的是 <code>1000ms</code>，也就是第一轮的超时时间是 1 秒，那么根本不需要重传 15 次，重传总间隔时长就会超过 <code>924600ms</code>。</li></ul><p>最小 RTO 和最大 RTO 已经是在 Linux 内核中定义好了：</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TCP_RTO_MAX ((unsigned)(120*HZ))</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TCP_RTO_MIN ((unsigned)(HZ/5))</span></span><br></pre></td></tr></tbody></table></figure><p>Linux <code>2.6+</code> 使用 1000 毫秒的 HZ，因此 <code>TCP_RTO_MIN</code> 约为 200 毫秒，<code>TCP_RTO_MAX</code> 约为 120 秒。</p><p>如果 <code>tcp_retries</code> 设置为 15，且 RTT 比较小，那么 RTO 初始值就约等于下限 <code>200ms</code>，这意味着它需要 <code>924.6</code> 秒才会将断开的 TCP 连接并通知给上层（即应用程序），每一轮的 RTO 增长关系如下表格：</p><table><thead><tr><th align="center">Retransmission</th><th align="center">RTO (ms)</th><th align="center">Time before a timeout (secs)</th><th align="center">Time before a timeout (mins)</th></tr></thead><tbody><tr><td align="center">1</td><td align="center">200</td><td align="center">0.2 secs</td><td align="center">0.0 mins</td></tr><tr><td align="center">2</td><td align="center">400</td><td align="center">0.6 secs</td><td align="center">0.0 mins</td></tr><tr><td align="center">3</td><td align="center">800</td><td align="center">1.4 secs</td><td align="center">0.0 mins</td></tr><tr><td align="center">4</td><td align="center">1600</td><td align="center">3.0 secs</td><td align="center">0.1 mins</td></tr><tr><td align="center">5</td><td align="center">3200</td><td align="center">6.2 secs</td><td align="center">0.1 mins</td></tr><tr><td align="center">6</td><td align="center">6400</td><td align="center">12.6 secs</td><td align="center">0.2 mins</td></tr><tr><td align="center">7</td><td align="center">12800</td><td align="center">25.4 secs</td><td align="center">0.4 mins</td></tr><tr><td align="center">8</td><td align="center">25600</td><td align="center">51.0 secs</td><td align="center">0.9 mins</td></tr><tr><td align="center">9</td><td align="center">51200</td><td align="center">102.2 secs</td><td align="center">1.7 mins</td></tr><tr><td align="center">10</td><td align="center">102400</td><td align="center">204.6 secs</td><td align="center">3.4 mins</td></tr><tr><td align="center">11</td><td align="center">120000</td><td align="center">324.6 secs</td><td align="center">5.4 mins</td></tr><tr><td align="center">12</td><td align="center">120000</td><td align="center">444.6 secs</td><td align="center">7.4 mins</td></tr><tr><td align="center">13</td><td align="center">120000</td><td align="center">564.6 secs</td><td align="center">9.4 mins</td></tr><tr><td align="center">14</td><td align="center">120000</td><td align="center">684.6 secs</td><td align="center">11.4 mins</td></tr><tr><td align="center">15</td><td align="center">120000</td><td align="center">804.6 secs</td><td align="center">13.4 mins</td></tr><tr><td align="center">16</td><td align="center">120000</td><td align="center">924.6 secs</td><td align="center">15.4 mins</td></tr></tbody></table><blockquote><p>总结</p></blockquote><p>如果「客户端进程崩溃」，客户端的进程在发生崩溃的时候，内核会发送 FIN 报文，与服务端进行四次挥手，以此断开 TCP 连接。</p><p>如果「客户端主机宕机」，那么是不会发生四次挥手的，具体后续会发生什么，还要看服务端会不会发送数据。</p><ul><li>如果服务端会发送数据，由于客户端已经不存在，收不到数据报文的响应报文，服务端的数据报文会超时重传，当重传总间隔时长达到一定阈值（内核会根据 <code>tcp_retries2</code> 设置的值计算出一个阈值）后，会断开 TCP 连接；</li><li>如果服务端一直不会发送数据，再看服务端有没有开启 TCP Keepalive 机制？<ul><li>如果服务端有开启 TCP Keepalive 机制，服务端在一段时间没有进行数据交互时，会触发 TCP Keepalive 机制，探测对方是否存在，如果探测到对方已经消亡，则会断开自身的 TCP 连接；</li><li>如果服务端没有开启 TCP Keep 机制，服务端的 TCP 连接会一直存在，并且一直保持在 <code>ESTABLISHED</code> 状态。</li></ul></li></ul>]]></content>
    
    
    <summary type="html">本文主要记录 Linux 网络编程笔记。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="开发随笔" scheme="https://www.techgrow.cn/tags/%E5%BC%80%E5%8F%91%E9%9A%8F%E7%AC%94/"/>
    
    <category term="Linux系统编程" scheme="https://www.techgrow.cn/tags/Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B/"/>
    
    <category term="网络编程" scheme="https://www.techgrow.cn/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>2026 年 7 款最佳免费 AI 编程工具</title>
    <link href="https://www.techgrow.cn/posts/e30e647f.html"/>
    <id>https://www.techgrow.cn/posts/e30e647f.html</id>
    <published>2026-03-15T14:25:49.000Z</published>
    <updated>2026-03-15T14:25:49.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.nxcode.io/zh/resources/news/best-free-ai-coding-tools-2026">2026 年 7 款最佳免费 AI 编程工具</a></li><li><a href="https://www.cnblogs.com/txycsig/p/21462572">Trae 国内版全面限额后，免费额度到底还能撑多久</a></li></ul>]]></content>
    
    
    <summary type="html">2026 年 7 款最佳免费 AI 编程工具，包括 GitHub Copilot、Cursor、OpenCode、Continue、Aider。</summary>
    
    
    
    
    <category term="开发工具" scheme="https://www.techgrow.cn/tags/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    <category term="AI" scheme="https://www.techgrow.cn/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>全球最强 AI 编程工具排行榜（2026 年）</title>
    <link href="https://www.techgrow.cn/posts/98d1001d.html"/>
    <id>https://www.techgrow.cn/posts/98d1001d.html</id>
    <published>2026-03-11T14:25:49.000Z</published>
    <updated>2026-03-11T14:25:49.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>由于人工智能（AI）发展迅速、日新月异，文中所列 AI 编程工具排行榜仅供参考，具有一定时效性，可能会随时间变化而失去准确性。截止 2026 年 3 月，AI 编程工具已经从早期单纯的 “代码补全工具”（只能基于上下文提示补几行或者一段代码），演进为具备更强自主能力的 AI Agent。这类 AI Agent 不仅能够理解需求，还可以自动完成从方案设计、代码编写、调试测试到迭代优化的一整套开发流程，甚至可以调用工具链、运行代码并根据结果进行自我修正。换句话说，AI 正在从 “辅助写代码” 转变为 “参与甚至主导功能开发” 的角色，极大提升了开发效率，也正在重塑软件工程的工作方式。</p><span id="more"></span><div class="admonition note"><p class="admonition-title">AI 编程工具的基础能力</p><ul><li>(1) 代码补全：在编码过程中提供单行或多行的代码推荐，并支持通过注释生成代码片段，提升代码编写速度。</li><li>(2) 代码解释：精确解释项目代码，帮助开发人员快速熟悉项目。</li><li>(3) 单测生成：为选中函数生成单测，提升单测覆盖率，提升代码质量。</li><li>(4) 注释生成：为整个函数或每行代码生成注释，提升代码可读性，方便协同开发。</li><li>(5) 智能修复：一键修改代码 bug，提升代码修复效率。</li><li>(6) 智能问答：针对研发领域定向优化问答质量，提供更精准的问答结果。</li></ul></div><h2 id="AI-编程工具的三种形态"><a href="#AI-编程工具的三种形态" class="headerlink" title="AI 编程工具的三种形态"></a>AI 编程工具的三种形态</h2><table><thead><tr><th>维度</th><th> Copilot（以 GitHub Copilot 为代表）</th><th>AI IDE（以 Cursor 为代表）</th><th>AI Agent（以 Codex 为代表）</th></tr></thead><tbody><tr><td>核心定位</td><td>写代码辅助</td><td>人机协作开发环境</td><td>自动化开发执行者</td></tr><tr><td>产品形态</td><td> IDE 插件</td><td> IDE（VSCode 分支）</td><td>云端 Agent / CLI</td></tr><tr><td> 控制权</td><td>完全在你</td><td>人机协同</td><td> AI 主导</td></tr><tr><td>工作方式</td><td>你写 → 它补全</td><td>你说 → 一起改</td><td>你说 → 它完成</td></tr><tr><td>上下文理解</td><td>低（局部代码）</td><td>中（项目级）</td><td>高（全项目 + 任务）</td></tr><tr><td>是否能自主规划</td><td>不会</td><td>有一定能力（弱 Agent）</td><td>强（任务拆解 + 执行）</td></tr><tr><td>是否自动执行命令</td><td>不支持</td><td>部分支持</td><td>支持（跑测试 / 改代码 / 提 PR）</td></tr><tr><td>典型场景</td><td>写函数、补代码</td><td>重构、调试、解释代码</td><td>从 0 开发模块、自动修复</td></tr><tr><td>学习成本</td><td>低</td><td>中等</td><td>较高</td></tr><tr><td>可控性</td><td>极强</td><td>较强</td><td>较弱</td></tr><tr><td>效率提升点</td><td>提速写代码</td><td>提升开发体验</td><td>替你完成开发任务</td></tr><tr><td>类比行为</td><td>代码自动补全</td><td>副驾驶</td><td>代驾司机</td></tr></tbody></table><hr><table><thead><tr><th>类型</th><th>类比角色</th><th>行为</th><th>使用场景</th></tr></thead><tbody><tr><td> Copilot</td><td> 实习生</td><td>你写一句，他帮你补一句</td><td>日常写代码 → Copilot 就够</td></tr><tr><td> Cursor</td><td> 高级同事</td><td>你们一起改代码、讨论方案</td><td>复杂项目开发 → Cursor 最舒服</td></tr><tr><td> Codex</td><td> 外包团队</td><td>你提需求，它直接交付结果</td><td>自动生成系统 / 批量任务 → Codex 最强</td></tr></tbody></table><h2 id="全球-Top-6-AI-编程工具"><a href="#全球-Top-6-AI-编程工具" class="headerlink" title="全球 Top 6 AI 编程工具"></a>全球 Top 6 AI 编程工具</h2><table><thead><tr><th>全球排名</th><th>名称</th><th>公司</th><th>类型</th><th>使用场景</th></tr></thead><tbody><tr><td> 1</td><td>Claude Code</td><td>Anthropic</td><td> 超级 Agent</td><td> 复杂系统设计：架构级开发、深度推理</td></tr><tr><td> 2</td><td>Codex</td><td>OpenAI</td><td> 工程 Agent</td><td>PR 执行 / 并行任务 / 自动化开发</td></tr><tr><td> 3</td><td>Cursor</td><td>Anysphere</td><td>IDE 型</td><td>日常开发主力：项目级编码 + AI IDE</td></tr><tr><td>4</td><td>GitHub Copilot</td><td>Microsoft</td><td> 工具型</td><td>代码补全 / 企业标准开发工具</td></tr><tr><td> 5</td><td>Windsurf</td><td>Codeium</td><td> 工程型</td><td>超大代码库维护 / 团队工程</td></tr><tr><td> 6</td><td>Replit AI</td><td>Replit</td><td> 产品型</td><td>快速做产品 / 一键生成并部署项目</td></tr></tbody></table><h3 id="Claude-Code（Anthropic）"><a href="#Claude-Code（Anthropic）" class="headerlink" title="Claude Code（Anthropic）"></a>Claude Code（Anthropic）</h3><p>最强 “AI 程序员” 本体（Agent 级别）</p><ul><li>综合能力：<ul><li>目前在多项编程与 Agent 评测中排名第一梯队</li><li>可独立完成复杂软件开发任务</li></ul></li><li>特点：<ul><li>直接 “接管任务”：能够自动写功能、改代码、跑测试</li><li>支持超大上下文（10 万 + 行代码）</li><li>多文件重构能力非常强</li><li>本质：不是辅助，而是 “替你干活”</li><li> 更偏 “系统级开发 Agent” ，而不是代码补全工具</li></ul></li><li>优点：<ul><li>极强的任务执行能力（从需求到代码闭环）</li><li>复杂逻辑推理能力强，适合架构级问题</li><li>多文件协同修改能力领先</li><li>对工程任务自动化支持很强</li></ul></li><li>缺点：<ul><li>对 UI / 产品体验类任务不如 Replit AI 直观</li><li>对任务描述质量要求较高（需要清晰拆解）</li><li>使用成本相对较高（学习 Agent 工作方式）</li></ul></li><li>适合：<ul><li>架构设计</li><li>大型项目重构</li><li> AI Agent 开发</li><li>复杂工程任务自动化</li></ul></li></ul><h3 id="Codex（OpenAI）"><a href="#Codex（OpenAI）" class="headerlink" title="Codex（OpenAI）"></a>Codex（OpenAI）</h3><p>最强 “AI 软件工程 Agent”（云端并行开发系统）</p><ul><li>综合能力：<ul><li>面向真实工程任务的 AI 编程 Agent</li><li> 可并行处理多个开发任务（PR / Feature / Bug Fix）</li><li>在复杂代码库中表现稳定（跨任务 PR 通过率较高）</li></ul></li><li>特点：<ul><li>云端沙盒执行：在独立环境中写代码、跑测试、提交 PR</li><li> 支持多 Agent 并行工作（Worktree / 多线程任务）</li><li>可自动完成：功能开发 / Bug 修复 / 代码迁移 / 重构</li><li>内置测试驱动执行（会跑测试直到通过）</li><li>面向 “端到端软件交付”，而不是单次代码生成</li><li>已支持 IDE / CLI / 云端多入口使用</li><li>可在后台自动执行长期工程任务</li></ul></li><li>优点：<ul><li>工程执行能力极强（可直接产出可合并 PR）</li><li>支持大规模代码库任务拆解与并行处理</li><li>自动化程度高，可持续运行在后台</li><li>非常适合真实团队开发流程（Git Workflow 友好）</li></ul></li><li>缺点：<ul><li>对非结构化需求（创意 / 产品想法）不如 Replit AI 直接</li><li>需要较规范的代码仓库结构才能发挥最佳效果</li><li>更偏工程协作，不适合 “零基础快速做 Demo” </li><li>对任务拆解能力要求较高</li></ul></li><li>适合：<ul><li>大型软件项目开发</li><li>工程级重构 / 迁移 / Bug 修复</li><li>团队并行开发（多任务 PR 管理）</li><li>AI 驱动的软件工程自动化</li><li>复杂后端 / 系统级项目维护</li></ul></li></ul><h3 id="Cursor（AI-原生-IDE）"><a href="#Cursor（AI-原生-IDE）" class="headerlink" title="Cursor（AI 原生 IDE）"></a>Cursor（AI 原生 IDE）</h3><p>最强 AI 原生 IDE（开发者体验天花板）</p><ul><li>综合排名：<ul><li>常年排名 Top 1 ~ 2（AI IDE 领域）</li></ul></li><li>综合能力：<ul><li>基于 VSCode 深度重构的 AI IDE</li><li>AI 深度融入编辑器，而不是插件形式</li><li>支持全代码库理解与跨文件编辑</li><li>内置多模型能力（Claude Code / GPT / Gemini 等）</li></ul></li><li>特点：<ul><li>支持 Agent 模式（自动改代码 + 重构 + 任务执行）</li><li>预测 “下一步修改”（不仅是单行补全）</li><li>可理解整个项目结构</li><li>支持多文件级别编辑与系统性重构</li><li> IDE + AI 深度融合，交互体验非常流畅</li></ul></li><li>优点：<ul><li>项目理解能力强（比 Copilot 更深入）</li><li>开发体验极佳（接近 “AI 操作 IDE” ）</li><li>非常适合全栈开发与高频编码场景</li><li>对中小型项目效率提升非常明显</li></ul></li><li>缺点：<ul><li>对超大规模工程（百万行级）不如 Windsurf</li><li> 自动执行复杂工程任务能力不如 AI Agent（如 Claude Code / Codex）</li><li>企业级权限与合规能力相对较弱</li></ul></li><li>适合：<ul><li>日常开发主力工具</li><li>独立开发者 / 一人公司</li><li>全栈工程师</li><li>中小型项目开发</li><li>高效率迭代型开发场景</li></ul></li></ul><h3 id="GitHub-Copilot（编程助手）"><a href="#GitHub-Copilot（编程助手）" class="headerlink" title="GitHub Copilot（编程助手）"></a>GitHub Copilot（编程助手）</h3><p>最普及、最稳定的 AI 编程助手</p><ul><li>使用率：<ul><li>市场使用率全球第一（行业标准工具）</li></ul></li><li>功能：<ul><li>代码补全</li><li>代码生成</li><li>代码重构</li><li>代码解释</li></ul></li><li>特点：<ul><li>最强 IDE 兼容性（VSCode / JetBrains / Neovim）</li><li>GitHub 全流程集成（PR、CI、代码评审）</li><li>自动补全速度最快</li><li>学习成本最低，上手最快</li></ul></li><li>优点：<ul><li>稳定可靠，企业接受度最高</li><li>集成生态完善（GitHub 工作流）</li><li>响应速度快</li></ul></li><li>缺点：<ul><li>上下文理解能力不如 Claude Code / Cursor</li><li> 更偏 “代码补全型工具”，而非任务型 Agent</li></ul></li><li> 适合：<ul><li>企业开发</li><li>多语言项目</li><li>稳定优先</li><li>代码补全为主的开发场景</li></ul></li></ul><h3 id="Windsurf（超大代码库）"><a href="#Windsurf（超大代码库）" class="headerlink" title="Windsurf（超大代码库）"></a>Windsurf（超大代码库）</h3><p>超大代码库处理王者</p><ul><li>特点：<ul><li>支持 “百万行代码级别” 理解</li><li>面向大型工程与团队协作设计</li><li>多模型支持（Claude Code / GPT 等）</li><li>强调代码库级别的理解与改造能力</li></ul></li><li>优点：<ul><li>超大仓库理解能力强</li><li>更偏工程级（系统维护能力强）</li><li>适合复杂代码重构</li></ul></li><li>缺点：<ul><li>学习成本略高</li><li>产品体验不如 Cursor 轻量直观</li></ul></li><li>适合：<ul><li>大厂项目</li><li>微服务 / 单体巨型仓库</li><li>团队协作开发</li><li>超大规模工程维护</li></ul></li></ul><h3 id="Replit-AI（云端开发）"><a href="#Replit-AI（云端开发）" class="headerlink" title="Replit AI（云端开发）"></a>Replit AI（云端开发）</h3><p>零环境（在线 IDE） + AI 自动生成项目</p><ul><li>特点：<ul><li>浏览器内直接开发，无需本地环境配置（零环境）</li><li>内置在线 IDE，支持实时编写、运行和调试代码</li><li>一句话生成完整项目（类似 Bolt / V0）</li><li>AI 自动完成依赖安装、构建与部署</li><li>提供从开发 → 运行 → 发布的一体化云端流程</li><li>集成 AI Agent，可自动生成完整应用（前端 + 后端 + 数据库）</li></ul></li><li>优点：<ul><li>极快完成 MVP（从想法到上线仅需几分钟）</li><li>大幅降低开发门槛（无需环境配置和运维）</li><li>适合快速试错与产品验证</li><li>全流程（开发 + 运行 + 部署一体化）在线完成，只需浏览器</li></ul></li><li>缺点：<ul><li>不适合大型复杂工程长期维护</li><li>自定义底层能力较弱（受平台限制）</li><li>AI 生成项目在复杂场景下可控性一般</li></ul></li><li>适合：<ul><li>初学者（无需复杂环境即可上手）</li><li>快速原型开发（Prototype / Demo）</li><li>SaaS / Web 小型项目快速上线</li><li>独立开发者 / 副业项目 / 验证想法</li></ul></li></ul><h2 id="国内-Top-4-AI-编程工具"><a href="#国内-Top-4-AI-编程工具" class="headerlink" title="国内 Top 4 AI 编程工具"></a>国内 Top 4 AI 编程工具</h2><table><thead><tr><th>国内排名</th><th>名称</th><th>公司</th><th>类型</th><th>使用场景</th></tr></thead><tbody><tr><td> 1</td><td> 通义灵码</td><td>阿里</td><td>工具 + IDE</td><td> 企业级开发：Java / 后端工程 / 代码补全标准化</td></tr><tr><td> 2</td><td>Trae</td><td> 字节跳动</td><td> AI IDE</td><td>AI 原生开发：项目级编码 / Agent 自动开发</td></tr><tr><td> 3</td><td>CodeBuddy</td><td> 腾讯</td><td>工程平台</td><td>企业研发：DevOps / 代码审查 / 流程化开发</td></tr><tr><td> 4</td><td> 文心快码</td><td>百度</td><td>工具型</td><td>代码补全 / 企业内部开发 / 规范化工程支持</td></tr></tbody></table><h3 id="通义灵码（阿里）"><a href="#通义灵码（阿里）" class="headerlink" title="通义灵码（阿里）"></a>通义灵码（阿里）</h3><p>国内最主流的 AI 编程助手（企业级使用率最高）</p><ul><li>产品形态：<ul><li>通义灵码插件（当前主力）<ul><li>写代码助手</li><li>类似 GitHub Copilot</li></ul></li><li> 通义灵码 AI IDE（新一代形态）<ul><li>自带 Agent 能力</li><li>类似 Cursor</li></ul></li><li>Qwen Code（独立 Agent 工具链）<ul><li>真正执行任务的 AI Agent</li><li> 类似 Codex</li></ul></li></ul></li><li> 综合能力：<ul><li>基于 Qwen 编码模型（Qwen-Coder 系列）</li><li>深度集成 VSCode / JetBrains / IDEA</li><li> 支持代码补全、生成、解释、单测生成</li></ul></li><li>特点：<ul><li>中文理解能力强（需求转代码能力优秀）</li><li>企业内部代码库适配能力较好</li><li>支持私有化部署（大厂常用）</li><li>与阿里云生态（IDE / DevOps）结合紧密</li></ul></li><li>优点：<ul><li>中文场景体验最好（需求理解准确）</li><li>企业级稳定性强</li><li>对 Java / 后端工程支持非常成熟</li><li>适合团队统一接入</li></ul></li><li>缺点：<ul><li>Agent 能力相对 Claude Code / Cursor 偏弱</li><li>超大代码库理解能力一般</li><li>创意型开发能力不强</li></ul></li><li>适合：<ul><li>企业后端开发（Java / Go）</li><li>国内互联网公司团队开发</li><li>规范化工程项目</li><li>内部系统开发</li></ul></li></ul><h3 id="Trae（字节跳动）"><a href="#Trae（字节跳动）" class="headerlink" title="Trae（字节跳动）"></a>Trae（字节跳动）</h3><p>面向未来 AI Agent 编程方向的 IDE（成长最快）</p><ul><li><p>产品形态：</p><ul><li>MarsCode 插件（早期阶段）<ul><li>写代码助手</li><li>类似 GitHub Copilot</li></ul></li><li>Trae AI IDE（最新阶段）<ul><li>独立 AI 原生 IDE（开发环境）</li><li>类似 Cursor / Windsurf</li></ul></li></ul></li><li><p> 综合能力：</p><ul><li>字节自研 AI IDE + 编程 Agent 系统</li><li>支持自动生成项目 + 修改代码 + 调试</li><li>与云开发环境结合</li></ul></li><li><p>特点：</p><ul><li>强调 AI Agent 自动开发能力</li><li>支持从需求到项目生成</li><li>与字节内部工程体系类似（Monorepo 友好）</li><li>持续强化多文件重构能力</li></ul></li><li><p>优点：</p><ul><li>AI Agent 方向发展最快之一</li><li>对前端 / 全栈项目非常友好</li><li>自动生成项目能力强</li></ul></li><li><p>缺点：</p><ul><li>生态仍在快速演进阶段</li><li>稳定性不如 Cursor / Copilot</li><li> 企业级成熟度较低</li></ul></li><li><p>适合：</p><ul><li>前端开发</li><li>快速原型开发</li><li> AI 驱动产品团队</li><li>探索型开发者</li></ul></li></ul><h3 id="CodeBuddy（腾讯）"><a href="#CodeBuddy（腾讯）" class="headerlink" title="CodeBuddy（腾讯）"></a>CodeBuddy（腾讯）</h3><p>国内新一代企业级 AI 编程平台</p><ul><li>综合能力：<ul><li>腾讯 + 混元模型 + 工程平台结合</li><li>支持代码生成、Review、测试生成</li><li>深度集成腾讯内部研发体系</li></ul></li><li>特点：<ul><li>面向企业研发流程（DevOps 一体化）</li><li>支持代码审查（AI Review）</li><li>与腾讯 CI/CD 强绑定</li><li>强调研发规范与工程质量</li></ul></li><li>优点：<ul><li>企业级流程支持完善</li><li>适合大型研发组织</li><li>安全与权限体系成熟</li></ul></li><li>缺点：<ul><li>IDE 体验不如 Cursor</li><li> 创作型开发能力一般</li><li>对个人开发者吸引力较弱</li></ul></li><li>适合：<ul><li>大型企业研发团队</li><li>金融 / 通信 / 政企系统</li><li>强流程规范开发</li></ul></li></ul><h3 id="文心快码（百度）"><a href="#文心快码（百度）" class="headerlink" title="文心快码（百度）"></a>文心快码（百度）</h3><p>偏向工程补全 + 企业研发工具型 AI 编程助手</p><ul><li>综合能力：<ul><li>基于文心大模型（ERNIE Code）</li><li>支持代码补全、生成、修复</li><li>与百度智能云开发体系集成</li></ul></li><li>特点：<ul><li>强代码补全能力（类似 Copilot）</li><li>支持多语言开发（Java / Python / JS）</li><li>企业内部代码场景适配</li><li>支持安全审计与代码规范检查</li></ul></li><li>优点：<ul><li>稳定可靠（偏传统工程工具）</li><li>在企业内网环境适配较好</li><li>适合规范开发流程</li></ul></li><li>缺点：<ul><li>Agent 能力较弱</li><li> IDE 体验一般</li><li>不适合复杂系统级开发</li></ul></li><li>适合：<ul><li>企业后端开发</li><li>传统研发团队</li><li>代码规范要求较高的项目</li></ul></li></ul><h2 id="国内-AI-编程工具的产品形态"><a href="#国内-AI-编程工具的产品形态" class="headerlink" title="国内 AI 编程工具的产品形态"></a>国内 AI 编程工具的产品形态</h2><h3 id="通义灵码（阿里）-1"><a href="#通义灵码（阿里）-1" class="headerlink" title="通义灵码（阿里）"></a>通义灵码（阿里）</h3><p>阿里的通义灵码有三种产品形态，本质是 “三层进化路线”，从插件工具 → AI IDE（开发环境） → AI Agent 执行系统。</p><ul><li><p><strong>通义灵码插件（当前主力）</strong></p><ul><li>产品形态：<ul><li>VSCode / JetBrains 插件</li></ul></li><li>能力特点：<ul><li>代码补全</li><li>基础代码生成与修复</li><li> IDE 内增强辅助能力</li><li> AI 问答（解释代码 / 调试代码）</li></ul></li><li>产品定位：<ul><li>AI 辅助写代码工具</li><li>类似 GitHub Copilot</li></ul></li></ul></li><li><p><strong> 通义灵码 AI IDE（新一代形态）</strong></p><ul><li>产品形态：<ul><li>基于 VSCode / JetBrains 深度改造的 AI IDE</li><li> 或独立 AI IDE 客户端</li></ul></li><li>能力特点：<ul><li>内置 AI Agent</li><li> 支持跨文件修改</li><li>支持任务式编程</li><li>可理解整个项目结构</li><li>支持 “需求 → 功能实现” 流程</li></ul></li><li>定位：<ul><li>类似 Cursor / Windsurf</li></ul></li><li> 核心变化：<ul><li>AI 参与开发流程，而不是只辅助编码</li><li>从 “写代码工具” 逐渐发展为 “任务执行型开发系统”</li></ul></li></ul></li><li><p><strong>Qwen Code（独立 Agent 工具链）</strong></p><ul><li>产品形态：<ul><li>CLI / 命令行 Agent</li><li> 可以脱离 IDE 独立运行</li><li>更偏向自动化执行系统</li></ul></li><li>能力特点：<ul><li>修改代码文件</li><li>执行 Shell 命令</li><li>运行测试与任务流程</li><li>可用于自动化工程操作</li></ul></li><li>定位：<ul><li>类似 Codex</li></ul></li><li> 核心变化：<ul><li>真正执行任务的 AI Agent</li></ul></li></ul></li><li><p><strong> 通义灵码三种产品形态的关系</strong></p><ul><li>通义灵码（插件）<ul><li>写代码助手</li><li> AI 辅助写代码（GitHub Copilot 模式）</li></ul></li><li>通义灵码 AI IDE<ul><li>AI 开发环境</li><li> AI 主导开发（Cursor 模式）</li></ul></li><li>Qwen Code<ul><li>AI 执行引擎</li><li> AI 执行任务（Agent / CLI 模式）</li></ul></li></ul></li></ul><h3 id="Trae（字节跳动）-1"><a href="#Trae（字节跳动）-1" class="headerlink" title="Trae（字节跳动）"></a>Trae（字节跳动）</h3><ul><li><p><strong>MarsCode（早期插件阶段）</strong></p><ul><li>产品形态：<ul><li>VSCode / JetBrains 插件</li></ul></li><li>核心功能：<ul><li>代码补全</li><li>代码生成</li><li> Bug 修复建议</li></ul></li><li>能力特点：<ul><li>以辅助编码为主</li><li>基于上下文的局部代码理解</li></ul></li><li>定位：<ul><li>类似 GitHub Copilot</li></ul></li><li> 核心模式：<ul><li>人主导，AI 辅助</li></ul></li></ul></li><li><p><strong> Trae（最新 AI IDE 阶段）</strong></p><ul><li>产品形态：<ul><li>独立 AI 原生 IDE（开发环境）</li></ul></li><li>核心功能：<ul><li>全项目代码理解</li><li>多文件级别修改与重构</li><li> AI Agent 自动写功能</li><li> Builder 模式（需求 → 项目生成）</li></ul></li><li>能力特点：<ul><li>支持任务级编程</li><li>可进行跨文件、跨模块的系统性改造</li><li>更接近 “AI 参与完整开发流程”</li></ul></li><li> 定位：<ul><li>类似 Cursor / Windsurf</li></ul></li><li> 核心模式：<ul><li>AI 主导，人类协作</li></ul></li></ul></li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://juejin.cn/post/7632632248769953798">2026 年 5 个免费 AI 编程工具横评</a></li><li><a href="https://aicoding.csdn.net/696edefda16c6648a983a0ec.html">2026 年 1 月 99 个最佳免费 AI 编程智能体与平台</a></li></ul>]]></content>
    
    
    <summary type="html">全球和国内最强 AI 编程工具排行榜（2026 年），比如 Claude Code、Codex、Cursor、GitHub Copilot。</summary>
    
    
    
    
    <category term="开发工具" scheme="https://www.techgrow.cn/tags/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    <category term="AI" scheme="https://www.techgrow.cn/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>电⼦元器件零基础⼊⻔教程之二</title>
    <link href="https://www.techgrow.cn/posts/c1303b78.html"/>
    <id>https://www.techgrow.cn/posts/c1303b78.html</id>
    <published>2026-01-30T13:03:50.000Z</published>
    <updated>2026-01-30T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/14c20f01.html">电⼦元器件零基础⼊⻔教程之一</a></li><li><a href="/posts/c1303b78.html">电⼦元器件零基础⼊⻔教程之二</a></li></ul><span id="more"></span><h2 id="常见电子元器件"><a href="#常见电子元器件" class="headerlink" title="常见电子元器件"></a>常见电子元器件</h2>]]></content>
    
    
    <summary type="html">本文主要介绍电⼦元器件零基础⼊⻔教程。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十四</title>
    <link href="https://www.techgrow.cn/posts/82bb2356.html"/>
    <id>https://www.techgrow.cn/posts/82bb2356.html</id>
    <published>2026-01-13T13:55:33.000Z</published>
    <updated>2026-01-13T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a>、<a href="/posts/8c35d945.html">C++ 从入门到精通之十二</a></li><li><a href="/posts/ce694f48.html">C++ 从入门到精通之十三</a>、<a href="/posts/82bb2356.html">C++ 从入门到精通之十四</a></li></ul><span id="more"></span>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>电⼦元器件零基础⼊⻔教程之一</title>
    <link href="https://www.techgrow.cn/posts/14c20f01.html"/>
    <id>https://www.techgrow.cn/posts/14c20f01.html</id>
    <published>2026-01-11T13:03:50.000Z</published>
    <updated>2026-01-11T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/14c20f01.html">电⼦元器件零基础⼊⻔教程之一</a></li><li><a href="/posts/c1303b78.html">电⼦元器件零基础⼊⻔教程之二</a></li></ul><span id="more"></span><h2 id="前序知识"><a href="#前序知识" class="headerlink" title="前序知识"></a>前序知识</h2><p>这里先了解⼀些原⼦相关的知识 ，会对电学中很多的概念和结论有更好的理解。</p><h3 id="原子（Atomic）"><a href="#原子（Atomic）" class="headerlink" title="原子（Atomic）"></a>原子（Atomic）</h3><ul><li>原⼦是构成物质的最⼩单位，它由原⼦核和电⼦组成，其中原⼦核又由质⼦和中⼦组成。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-1.png"></p><ul><li>下图为碳原⼦的简易模型图：</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-2.png"></p><div class="admonition note"><p class="admonition-title">提示</p><p>这里提及原⼦结构，是为了给后续的：电流、电压、⼆极管、三极管等知识做铺垫，但也会忽略掉⼀些与电学⽆关的知识，⽐如：有些元素（氢的同位素氕）没有中⼦，再⽐如：质⼦和中⼦的⽐例关系，会对元素的放射性有⼀定影响等，这些知识不在本教程中体现。</p></div><h3 id="质⼦（Proton）"><a href="#质⼦（Proton）" class="headerlink" title="质⼦（Proton）"></a>质⼦（Proton）</h3><ul><li>质⼦带⼀个单位的正电荷，且质⼦数量决定着元素身份，改变了质⼦数量，也就改变了原⼦的类型。</li><li>举个例⼦：如果能从铅的原⼦核（82 个质子）⾥移出 3 个质⼦，那就成功的⽤铅原⼦造出了⼀个⾦原⼦（79 个质子）。</li><li>⽽实际上：质⼦之间结合地⾮常紧密，⼏乎⽆法分开，这也是炼⾦术未能实现的原因。</li><li>拓展内容：现代物理学中的核反应堆、粒⼦加速器等可以实现上述过程，但所需能量和成本⾮常⾼，已经远超⻩⾦的价值了，⽽且这种反应通常很难精确控制，产出的黄⾦量可能很少。</li></ul><h3 id="中⼦（Neutron）"><a href="#中⼦（Neutron）" class="headerlink" title="中⼦（Neutron）"></a>中⼦（Neutron）</h3><ul><li>中⼦不带电，且中⼦对原⼦的影响远⼩于质⼦。</li><li>电学领域通常不对中⼦进⾏深⼊探讨，因为它不带电荷，不直接影响电势、电流的形成。</li></ul><h3 id="电⼦（Electron）"><a href="#电⼦（Electron）" class="headerlink" title="电⼦（Electron）"></a>电⼦（Electron）</h3><ul><li>电⼦带⼀个单位的负电荷，它围绕原⼦核做⽆规则的运动。<ul><li>早期的『波尔模型』，将电⼦的运动⽐作成⾏星的 “轨道运动”；</li><li>但随着量⼦⼒学的发展，这种⽐喻并不完全正确，所谓的 “轨道” 其实是电⼦出现的概率区域，并不不是⼀个具体的路径。</li></ul></li><li>通常来说原⼦的质⼦数和电⼦数相等，其内部的正电荷与负电荷互相抵消，这时候原⼦呈电中性。</li></ul><hr><ul><li>电⼦层：电⼦围绕原⼦核活动的范围。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-3.png"></p><ul><li><p>最外层电⼦数为 8 个时，通常是较稳定的状态。</p><ul><li>许多元素在化学反应中，会倾向于通过：获得电⼦、失去电⼦、共享电⼦，来尽可能达到 8 个电⼦的稳定结构，这个现象被称为：⼋隅规则。</li><li>当然也有⼀些例外的元素，例如：氢 (H) 、氦 (He) 只需要 2 个电⼦就能达到稳定状态。</li></ul></li><li><p>不同电荷之间互相吸引，相同电荷之间互相排斥，具体计算参考库伦定律：</p></li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-4.png"></p><ul><li>最外层的电⼦更容易脱离原⼦，因为它们与原⼦核的距离较远，受到的引⼒较弱。</li></ul><h3 id="⽯墨与⾦刚⽯"><a href="#⽯墨与⾦刚⽯" class="headerlink" title="⽯墨与⾦刚⽯"></a>⽯墨与⾦刚⽯</h3><ul><li>⽯墨：每个碳原⼦与周边 3 个碳原⼦分别建⽴共价键，最终外层剩余 1 个 未成键的电⼦，这个电⼦可以在不同的原⼦之间⾃由移动，所以⽯墨是导体。</li><li>⽯墨的层与层之间是靠『范德华⼒』结合在⼀起的，范德华⼒是⼀种弱的分⼦间作⽤⼒，因此⽯墨的各个层之间可以很容易地滑动，所以⽯墨可以作为：润滑剂、铅笔芯等。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-5.png"></p><hr><ul><li>⾦刚⽯：每个碳原⼦与周边 4 个 碳原⼦分别建⽴共价键，形成⼀个⾮常坚硬的⽴体⽹状结构，没有⾃由电⼦，所以⾦刚⽯是绝缘体。</li><li>⾦刚⽯的碳原⼦之间共价键内的电⼦活动范围相对较⼩，且共价键内的电⼦被两个原⼦核共同吸引，所以碳原⼦之间共价键内的电⼦相对稳定，不会轻易脱落，除⾮有⾜够的能量来打破这个键。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-6.png"></p><h2 id="电学基础"><a href="#电学基础" class="headerlink" title="电学基础"></a>电学基础</h2><h3 id="电场、电势、电势能"><a href="#电场、电势、电势能" class="headerlink" title="电场、电势、电势能"></a>电场、电势、电势能</h3><p>为了能更好的理解⼀些抽象的概念，这里⽤重⼒场中的相关概念，去类⽐电场中的相关概念。</p><h4 id="重⼒场、电场"><a href="#重⼒场、电场" class="headerlink" title="重⼒场、电场"></a>重⼒场、电场</h4><ul><li>重⼒场：在地球周围存在，有质量的物体进⼊该区域后，会受到被拉向中⼼的⼒。</li><li>电场：在电荷周围存在，带电的粒⼦进⼊这个区域后，会受到吸引⼒或排斥⼒。<ul><li>沿着电场线⽅向，电场的强度会逐渐减弱，且⽆论正电荷还是负电荷，⽆穷远处的电场强度为 0。</li></ul></li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-9.png"></p><h4 id="重⼒势能、电势能"><a href="#重⼒势能、电势能" class="headerlink" title="重⼒势能、电势能"></a>重⼒势能、电势能</h4><ul><li>重⼒势能：描述物体在重⼒场中所具备的能量，与所处位置、物体质量均有关。</li><li>电势能：描述带电粒⼦在电场中所具备的能量，与所处位置、粒⼦带电量均有关。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-10.png"></p><h4 id="重⼒势、电势"><a href="#重⼒势、电势" class="headerlink" title="重⼒势、电势"></a>重⼒势、电势</h4><p><img data-src="../../../asset/2026/01/embedded-dev-11.png"></p><p><img data-src="../../../asset/2026/01/embedded-dev-12.png"></p><h3 id="电流"><a href="#电流" class="headerlink" title="电流"></a>电流</h3><ul><li><p>电流的概念：</p><ul><li>电荷的定向移动，形成了电流，衡量电流的⼤⼩，要看单位时间内通过导体横截⾯的电荷量。</li><li>注意：<ul><li>电学上定义的『电流⽅向』是正电荷的流动的⽅向，这个概念在 19 世纪初就先被定电学上定义的『电流⽅向』是正电荷的流动的⽅向，这个概念在 19 世纪初就先被定义了；</li><li>后来虽然发现了是电⼦在移动，但为了保持定义的统⼀性，『电流⽅向』仍然⽤正电荷的流动去定义，<strong>所以『电流⽅向』与『电⼦移动⽅向』是相反的</strong>。</li></ul></li></ul></li><li><p>电流的单位：安培 (A)，简称安</p><ul><li>1A 的含义：1 秒钟内有 <code>6.242*10^18</code> 个单位电荷（电⼦、质⼦、都属于单位电荷）通过了导体横截⾯。</li><li><code>6.242*10^18</code> 个单位电荷所带的电荷总量，为 1 库伦 (C)，所以 1A 的含义也能这样描述：在 1 秒钟内，有 1 库仑的电荷通过了导体的横截⾯。</li></ul></li><li><p>电流的单位换算：</p><ul><li>进位为 1000，例如 1A = 1000mA</li><li> 常⽤的单位是：安 (A)、毫安 (mA)<br><img data-src="../../../asset/2026/01/embedded-dev-7.png"></li></ul></li><li><p>常⻅电器的⼯作电流：<br>  <img data-src="../../../asset/2026/01/embedded-dev-8.png"></p></li></ul><h3 id="电压"><a href="#电压" class="headerlink" title="电压"></a>电压</h3><ul><li><p>电压的概念：</p><ul><li>⼜称电势差，是两点之间电势的差值，衡量电压的⼤⼩，要看两点电势差的⼤⼩。</li><li>有了电压，电子才能持续且定向地移动起来，所以电压是形成电流的必要条件之一。</li><li>电压越大，能定向移动起来的电子就越多，电流就会越大。</li></ul></li><li><p>电压的单位：伏特 (V)，简称伏</p><ul><li>1V 的含义是：电场 对 1 库伦 (C) 电荷做了 1 焦⽿ (J) 的功。<br><img data-src="../../../asset/2026/01/embedded-dev-17.png"></li></ul></li><li><p>电压的单位换算：</p><ul><li>进位为 1000，例如 1kV = 1000V<br><img data-src="../../../asset/2026/01/embedded-dev-13.png"></li></ul></li><li><p>常⻅电源的电压：<br>  <img data-src="../../../asset/2026/01/embedded-dev-14.png"></p></li></ul><h3 id="电阻"><a href="#电阻" class="headerlink" title="电阻"></a>电阻</h3><ul><li><p>电阻的概念：</p><ul><li>电阻是指材料或元器件对电流流动的阻碍程度。</li><li>电阻的大小不仅与材料有关，还与材料的粗细、长短等因素有关。</li></ul></li><li><p>电阻的单位：欧姆（简称欧），符号是 Ω。</p><ul><li>1Ω 的含义：给导体施加 1V 电压，此时如果导体的电流为 1A，那这个导体的电阻就是 1Ω。</li></ul></li><li><p>电阻的单位换算：</p><ul><li>进位为 1000，例如 1KΩ = 1000Ω<br><img data-src="../../../asset/2026/01/embedded-dev-15.png"></li></ul></li><li><p>电阻的决定式：</p><ul><li>$R = \rho \cdot \frac{L}{A}$<ul><li><strong>R</strong>：电阻</li><li><strong> L</strong>：导体的⻓度</li><li><strong> A</strong>：导体的横截⾯积</li><li><strong> ρ</strong>：材料的电阻率，表示材料对电流的阻碍能⼒</li></ul></li></ul></li><li><p>电阻的补充内容：</p><ul><li>后续会讲解⼀种电⼦元器件 — 『电阻器』，它的简称也叫『电阻』，是⼀种专⻔⽤于控制电流大小的电⼦元器件（如下图所示），其的作⽤是：为电路提供电阻，来限制电流的⼤⼩。<br><img data-src="../../../asset/2026/01/embedded-dev-16.png"></li></ul></li></ul><h3 id="电路"><a href="#电路" class="headerlink" title="电路"></a>电路</h3><ul><li>电源：物理学中将提供电能的装置叫做电源。</li><li>⽤电器：将灯泡、电动机、等这类消耗电能的装置叫做⽤电器。</li><li>电路：电源、⽤电器，再加上导线、开关等，就组成了电流可以流过的路径，这就是电路。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-18.png"></p><ul><li><p>电路图：</p><ul><li>在绘制电路图时，为了绘制简单且⽅便研究，通常⽤图形符号来表示元件。</li><li>此处先只列举了三个电路符号（如下图所示），其他的电路符号以后会随着教程讲解。<br><img data-src="../../../asset/2026/01/embedded-dev-19.png"></li></ul></li><li><p>电路状态：</p><ul><li>断路：电路中某处被切断，电路中没有电流流过。</li><li>通路：正常接通的电路，⽤电器能够正常⼯作的电路。</li><li>短路：比如直接⽤导线将电源的正、负极连接起来（⚠️危险操作）。<br><img data-src="../../../asset/2026/01/embedded-dev-20.png"></li></ul></li></ul><h3 id="欧姆定律"><a href="#欧姆定律" class="headerlink" title="欧姆定律"></a>欧姆定律</h3><ul><li><p>概念：</p><ul><li>导体中的电流，与导体两端的电压成正⽐，与导体的电阻成反⽐。</li><li>在电阻不变的情况下，电压越⼤，电流也会越⼤。</li></ul></li><li><p>公式：</p><ul><li>$I = \frac{U}{R}$<ul><li><strong>U</strong> 为电压，单位为 <strong>V</strong></li><li><strong>I</strong> 为电流，单位为 <strong>A</strong></li><li><strong>R</strong> 为电阻，单位为 <strong>Ω</strong></li></ul></li><li> 变形公式一：<ul><li>$R = \frac{U}{I}$</li></ul></li><li> 变形公式二：<ul><li>$U = IR$</li></ul></li></ul></li><li><p> 决定式 vs 定义式：<br>  <img data-src="../../../asset/2026/01/embedded-dev-21.png"></p></li><li><p>使⽤场景：<br>  <img data-src="../../../asset/2026/01/embedded-dev-22.png"></p></li><li><p>局限性：</p><ul><li><strong>欧姆定律有局限性，仅适用于纯电阻电路（或者说纯电阻元器件、纯电阻设备）！</strong></li><li>纯电阻电路：消耗的电能仅转化为热能，没有其他形式的能量转换。</li></ul></li></ul><h3 id="直流电-VS-交流电"><a href="#直流电-VS-交流电" class="headerlink" title="直流电 VS 交流电"></a>直流电 VS 交流电</h3><h4 id="直流电"><a href="#直流电" class="headerlink" title="直流电"></a>直流电</h4><ul><li><p>定义：</p><ul><li>有固定的正负极，且电流⽅向始终『不变』的电流，全称：Direct Current，简称 DC。</li></ul></li><li><p>举例：</p><ul><li>⼲电池、锂电池、光伏发电板等。<br><img data-src="../../../asset/2026/01/embedded-dev-23.png"></li></ul></li></ul><h4 id="交流电"><a href="#交流电" class="headerlink" title="交流电"></a>交流电</h4><ul><li><p>定义：</p><ul><li>电流⽅向随时间做『周期性变化』的电流，全称：Alternating Current，简称 AC。</li><li>交流电的电流⽅向⼀定做周期性变化，但电流⼤⼩不⼀定做周期性变化！</li></ul></li><li><p>举例：</p><ul><li>家庭⽤电<br><img data-src="../../../asset/2026/01/embedded-dev-24.png"></li></ul></li></ul><h4 id="扩展内容"><a href="#扩展内容" class="headerlink" title="扩展内容"></a>扩展内容</h4><p>家庭⽤电为的 220V / 50Hz 的正弦交流电，下⾯是关于家⽤交流电的⼀些总结性内容。</p><ul><li><p>周期：</p><ul><li>交流电完成⼀个完整波动循环所需的时间（参考下图）。<br><img data-src="../../../asset/2026/01/embedded-dev-25.png"></li></ul></li><li><p>零线与⽕线：</p><ul><li>交流电⽆正负极之分，但有零⽕线之分。</li><li>零线的电势始终为 0，⽕线的电势不断变化，有时⾼于 0，有时低于 0，这样就形成了交变电流。</li></ul></li><li><p>对于 220V / 50Hz 的理解：</p><ul><li>220V 指的是交流电的有效电压<ul><li>什么是有效电压？—— 让交流电和直流电，分别通过同⼀个电阻，在交流电的⼀个周期内，若⼆者产⽣的热量相等，那么这个直流电的电压，就是交流电的有效电压。</li><li>我们平时聊天所说到的、⽤电器说明书上写的、电表所测到的，都是有效电压，即：220V。家庭用电的 220V 指的是交流电的有效电压。</li><li>什么是峰值电压？—— 交流电在⼀个周期中达到的最⼤电压值，220V 交流电的峰值电压为：$200\sqrt {2}$，约为：311V，<a href="../../../asset/2026/01/embedded-dev-30.png">如图所示</a>。</li></ul></li><li>50hz 指的是交流电的频率<ul><li> 50hz 的含义是交流电在 1 秒钟内完成了 50 个周期（每个周期包含两次电流⽅向变化，所以对于 50Hz 的交流电来说，1 秒钟内电流的⽅向变化了 100 次）。</li></ul></li></ul></li><li><p>整流与逆变：</p><ul><li>什么是整流？—— 交流（AC） → 直流（DC）</li><li>什么是逆变？—— 直流（DC） → 交流（AC）</li><li>在⽣活中很多的电器，都是将交流转换成直流后⼯作的（参考下图），例如：电脑、⼿机、路由器等，因为直流电更加稳定，适合精密电路。<br><img data-src="../../../asset/2026/01/embedded-dev-26.png"></li></ul></li></ul><h3 id="弱电与强电"><a href="#弱电与强电" class="headerlink" title="弱电与强电"></a>弱电与强电</h3><ul><li><p>弱电：</p><ul><li>弱电的电压⼀般较低，⾏业规定安全电压为不⾼于 36V。</li><li>弱电通常⽤于直流电路（3.3V、5V、12V），弱电也能于信号传递，例如：⾳频和视频线路、⽹络线路、电话线等（参考下图）。<br><img data-src="../../../asset/2026/01/embedded-dev-27.png"></li></ul></li><li><p>强电：</p><ul><li>强电的电压⼀般很⾼，对人体危害较大，必须采取防护措施，例如：220V 的家⽤电、1000V 及以上的⾼压电。</li><li>强电常⽤于能源传输或者高功率供电（参考下图）。<br><img data-src="../../../asset/2026/01/embedded-dev-28.png"></li></ul></li><li><p>通过人体电流的大小，直接决定了电击的危险性：</p><ul><li>感知电流：能引起人感觉的最小电流，约 <strong>0.7mA ~ 1.1mA</strong> 左右，有很轻微的刺痛感，但不会造成伤害。</li><li>摆脱电流：能自主摆脱的最大电流，约 <strong>10mA ~ 16mA</strong> 左右，会有明显的肌肉收缩，不要持续接触。</li><li>致命电流：能在短时间内危及生命的最小电流，约为 <strong>50mA</strong> 左右，明显的呼吸困难、心脏产生室颤。</li></ul></li></ul><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>⼤家⼀定要注意⽤电安全，⼀般弱电平台⽤⼿直接触摸不会造成⼈身危险；但是强电平台 ⼀定不能⽤⼿直接触摸，会有⽣命危险！</strong></p></div><h3 id="家庭电路"><a href="#家庭电路" class="headerlink" title="家庭电路"></a>家庭电路</h3><p>本节属于电⼯学内容，但也属于电学相关的常识性内容，不必深究，了解⼀下就可以。</p><p><img data-src="../../../asset/2026/01/embedded-dev-29.png"></p><ul><li>家庭电路中的『保护接地』，是如何保护我们的？<ul><li>(1) 发电⼚系统会通过⼀个接地点，将零线连接到⼤地，这是电⼒系统中⼀个重要的设计， 其⽬的是：<ul><li>保证电压稳定：将零线电压锁定为地电位（0V）。</li><li>提供保护：在发⽣故障时，接地可以快速引导电流回到地，触发保护装置（如断路器）。</li></ul></li><li>(2) 为什么触摸⽕线很危险？<ul><li>⽕线为 220V，地为 0V，身体与地之间构成回路，导致电流流过身体，很危险！</li><li>人体触电时，电流的整体路径是：⽕线（220V）→ 人体 → 地 → 零线（0V，接地）→ 发电⼚。</li></ul></li></ul></li></ul><hr><ul><li>无地线 + 人体触电（致命危险）</li></ul><p><img data-src="/gif/alternating-current-1.gif"></p><ul><li>接地线 + 人体触电（非致命危险）</li></ul><p><img data-src="/gif/alternating-current-2.gif"></p><ul><li>断路器 + 接地线 + 人体触电（安全）</li></ul><p><img data-src="/gif/alternating-current-3.gif"></p><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>特别注意，家庭⽤电属于强电！禁⽌⾃⼰操作强电！也禁⽌⽤⾃⼰的身体去验证各种结论！</strong></p></div><h3 id="串联与并联"><a href="#串联与并联" class="headerlink" title="串联与并联"></a>串联与并联</h3><h4 id="串联电路"><a href="#串联电路" class="headerlink" title="串联电路"></a>串联电路</h4><p><img data-src="../../../asset/2026/01/embedded-dev-31.png"></p><h4 id="并联电路"><a href="#并联电路" class="headerlink" title="并联电路"></a>并联电路</h4><p><img data-src="../../../asset/2026/01/embedded-dev-32.png"></p><h4 id="串并联练习"><a href="#串并联练习" class="headerlink" title="串并联练习"></a>串并联练习</h4><ul><li>电路图 1，请分析 A、B、C 三点电压分别是多少？答案是：A 点 9V，B 点 3V，C 点 0V，<a href="../../../asset/2026/01/embedded-dev-41.png">点击查看图解分析</a>。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-33.png"></p><ul><li>电路图 2，请分析 A、B、C 三点电压分别是多少？答案是：A 点 4V，B 点 1V，C 点 0V，<a href="../../../asset/2026/01/embedded-dev-42.png">点击查看图解分析</a>。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-34.png"></p><ul><li>电路图 3，请分析 A、B、C 三点电压分别是多少？答案是：A 点 10V，B 点 8V，C 点 4V，<a href="../../../asset/2026/01/embedded-dev-44.png">点击查看图解分析</a>。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-43.png"></p><div class="admonition note"><p class="admonition-title">电路仿真软件</p><ul><li>串联电路和并联电路可以使用开源电路仿真软件 <a href="https://github.com/sharpie7/circuitjs1">CircuitJS1</a> 进行模拟和分析。CircuitJS1 是基于 Paul Falstad 的 <a href="https://www.falstad.com/circuit/">Circuit Simulator</a> 开发的 JavaScript 版本。</li><li>其他的 CircuitJS1 开源衍生版本有：<a href="https://github.com/pfalstad/circuitjs1">pfalstad/circuitjs1</a>、<a href="https://github.com/SEVA77/circuitjs1">SEVA77/circuitjs1</a>。</li></ul></div><h3 id="电功率"><a href="#电功率" class="headerlink" title="电功率"></a>电功率</h3><ul><li><p>概念：</p><ul><li>电功率⽤于衡量电流在单位时间内所做的功（⽤来表示⽤电器消耗电能快慢的物理量）。</li></ul></li><li><p>单位：</p><ul><li>瓦特（简称瓦），符号是 W。</li></ul></li><li><p>单位换算：</p><ul><li>进位为 1000，例如： 1kW = 1000W。<br><img data-src="../../../asset/2026/01/embedded-dev-36.png"></li></ul></li><li><p>公式：</p><ul><li>$P = U I$<ul><li><code>U</code> 为电压，单位为 <code>V</code>（伏）</li><li><code>I</code> 为电流，单位为 <code>A</code>（安）</li><li><code>P</code> 为功率，单位为 <code>W</code>（瓦）</li></ul></li></ul></li><li><p>常⻅⽤电器的功率</p></li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-37.png"></p><hr><ul><li><p>瓦时 VS 千瓦时</p><ul><li>⼆者都是表示电能的⼀种单位。</li><li>瓦时：<ul><li>瓦时是功率（W）与时间（h）的乘积，即：<code>瓦时 = 功率 × 时间</code></li><li>⼀个 1W 的⽤电器，⼯作 1h ，它消耗的电量就是 1 瓦时（1Wh）。</li></ul></li><li>千瓦时：<ul><li>千瓦时是瓦时的千倍，通常⽤于描述⼤型电⼒设备，如电表读数、新能源汽⻋等。</li><li>⼀个 1kW 的电器⼯作 1h ，它消耗的电量就是 1 千瓦时（1kWh，俗称 1 度电）。<br><img data-src="../../../asset/2026/01/embedded-dev-38.png"></li></ul></li></ul></li><li><p>安时 VS 毫安时</p><ul><li>⼆者都是描述电池容量的单位。</li><li>安时：<ul><li>表示该电池能以 1A 的电流持续⼯作 1h，通常⽤于描述⼤容量的电池。</li></ul></li><li>毫安时：<ul><li>安时的千分之⼀，通常⽤于描述⼩容量的电池。</li></ul></li><li>例如：<ul><li>华为『Mate70 Pro』⼿机电池的容量为 5700mAh ，充电宝的电池容量为 20000mAh。</li><li>有些时候我们也省略 <code>h</code>，直接说：⼿机的电池容量和为 5700mA，充电宝电池容量为：20000mA。<br><img data-src="../../../asset/2026/01/embedded-dev-39.png"></li></ul></li></ul></li></ul><h3 id="焦⽿定律"><a href="#焦⽿定律" class="headerlink" title="焦⽿定律"></a>焦⽿定律</h3><ul><li><p>概念：</p><ul><li>电流通过导体产⽣的热量与电流的平⽅成正⽐，跟导体的电阻成正⽐，跟通电时间成正⽐。</li></ul></li><li><p>单位：</p><ul><li>单位：焦⽿（简称焦），简称 J。</li></ul></li><li><p>公式：</p><ul><li>$Q = I^2 R t$<ul><li><code>Q</code> 为热量，单位为：<code>J</code>（焦⽿）</li><li><code>I</code> 为电流，单位为：<code>A</code>（安培）</li><li><code>R</code> 为电阻，单位为：<code>Ω</code>（欧姆）</li><li><code>t</code> 为时间，单位为：<code>s</code>（秒）</li></ul></li></ul></li><li><p>例如：</p><ul><li>⼀根 60Ω 的电阻丝接在 36V 的电源两端，在 5 分钟内共产⽣多少热量？答案是：$ \left (\frac {36\ \text {V}}{60\ \Omega} \right)^2 \times 60\ \Omega \times 5\ \times 60\ = 6480\ \text {J} $<br>  <img data-src="../../../asset/2026/01/embedded-dev-40.png"></li></ul></li><li><p>注意：</p><ul><li>电路通过导体时，如果电能全部转化为内能，⽽没有同时转化为其他形式的能量，那么电流产⽣的热量就等于消耗的电能，但实际上：<strong>只有纯电阻电路，才能把电能完全⽤于产⽣热量</strong>！</li><li>对于<strong>纯电阻电路</strong>来说，消耗的电能全部⽤于转化热量，所以可以推导出如下公式：<ul><li>功率计算公式：$P = \frac {U^2}{R}$，$P = I^2 R$</li><li> 热量计算公式：$Q = U I t$，$Q = P t$</li></ul></li></ul></li></ul><h3 id="公式总结"><a href="#公式总结" class="headerlink" title="公式总结"></a>公式总结</h3><p><img data-src="../../../asset/2026/01/embedded-dev-45.png"></p><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>功率计算公式（$P = U I$）与焦耳定律（$Q = I^2 R t$）是原始公式，适用于所有电路。</li><li>欧姆定律（$I = \frac {U}{R}$）有局限性，仅适用于纯电阻电路（或者说纯电阻元器件、纯电阻设备）。</li></ul></div>]]></content>
    
    
    <summary type="html">本文主要介绍电⼦元器件零基础⼊⻔教程。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>基于 ESP32、SI4732 开发收音机</title>
    <link href="https://www.techgrow.cn/posts/c350c4dd.html"/>
    <id>https://www.techgrow.cn/posts/c350c4dd.html</id>
    <published>2026-01-10T13:03:50.000Z</published>
    <updated>2026-01-10T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="学习路线"><a href="#学习路线" class="headerlink" title="学习路线"></a>学习路线</h2><p>基于 ESP32 + SI4732 + ATS MINI 的生态开发收音机，开发者并不需要从 51 单片机、寄存器操作等传统嵌入式底层知识开始学习，而可以利用现有的开源项目成果，以 “应用集成和修改” 为核心来切入，这会大大降低门槛。</p><table><thead><tr><th>个人的兴趣 / 目标</th><th>推荐入口</th><th>需要优先掌握的核心技能</th><th>学习难度</th><th>可能成果</th></tr></thead><tbody><tr><td>只想使用 / 小改现有功能（如改界面、调参数）</td><td>作为高级用户</td><td><br> - 1. Arduino IDE 基础使用 <br> - 2. 代码编译与烧录 <br> - 3. 配置文件修改</td><td>⭐⭐ 入门</td><td>成功编译并烧录官方固件，修改频率范围、显示语言等。</td></tr><tr><td>想深度定制功能（如增加新模块、改交互逻辑）</td><td>作为项目开发者</td><td><br> - 1. Arduino 框架下 C++ 基础 <br> - 2. ESP32 基础外设使用（I2C， 旋钮编码器，显示屏）<br> - 3. 阅读理解开源代码的能力</td><td>⭐⭐⭐ 中等</td><td>为收音机增加蓝牙音频输出、修改菜单结构、支持新的显示屏。</td></tr><tr><td>想彻底重写 / 从底层理解（如研究算法、追求极致性能）</td><td>作为系统开发者</td><td><br> - 1. 扎实的 C/C++ 语言和数据结构 <br> - 2. ESP-IDF 框架体系 <br> - 3. 嵌入式操作系统基础 <br> - 4. 射频电路基础知识</td><td>⭐⭐⭐⭐⭐ 困难</td><td>独立架构收音机软件，优化 DSP 音频处理算法，或移植到其他平台。</td></tr></tbody></table><span id="more"></span><div class="admonition note"><p class="admonition-title">学习路线选择</p><ul><li>对于绝大多数爱好者来说，前两个入口（"高级用户" 和 "项目开发者"）是最实际、最有成就感的起点。</li></ul></div><h2 id="必备知识"><a href="#必备知识" class="headerlink" title="必备知识"></a>必备知识</h2><p>基于 ESP32 + SI4732 + ATS MINI 的生态开发收音机，开发者不需要学习 “底层嵌入式全家桶”（寄存器、启动流程、裸机 RTOS 移植等），会 Arduino API 就能开发，但理解 I2C、GPIO、库结构、编译烧录流程是必须的。</p><ul><li><p>必须掌握的知识（重点）</p><ul><li>ESP32 基础使用<ul><li>会刷固件（USB / 串口）</li><li>会选开发板（ESP32 / ESP32-S3）</li><li>会看 GPIO 引脚定义</li></ul></li><li> Arduino 开发方式<ul><li>开发工具：Arduino IDE / PlatformIO-IDE</li><li> 开发框架：Arduino Core for ESP32</li><li> 使用 <code>setup()</code> / <code>loop()</code></li><li>编译 → 烧录 → 串口调试</li><li>修改现有 <code>.ino</code> / <code>.cpp</code></li></ul></li><li>I2C 基本概念<ul><li> SDA / SCL</li><li>SI4732 是 I2C 设备</li><li>会填正确的引脚即可（不需要写 I2C 驱动）</li></ul></li><li>读文档与改配置<ul><li>屏幕分辨率</li><li>按键 / 编码器引脚</li><li> FM / AM / SW 频段参数</li></ul></li><li>到这里，就已经能跑 ATS MINI 固件了</li></ul></li><li><p>不需要掌握的知识（忽略）</p><ul><li>裸机开发</li><li>寄存器级 GPIO / I2C</li><li>FreeRTOS 调度原理</li><li>链接脚本 / 启动代码</li><li>芯片 DataSheet 逐页啃</li></ul></li><li><p>只有在下面情况之一，才建议系统学习嵌入式</p><ul><li>自己写 SI4732 驱动</li><li>自己做低功耗管理</li><li>不用 Arduino IDE，改用 ESP-IDF</li><li> 做商业级产品 / 深度定制</li></ul></li></ul><div class="admonition note"><p class="admonition-title">ESP32 与 SI4732 的关系</p><ul><li>ESP32 是主控芯片（MCU），SI4732 是收音机芯片（射频 + 解调）。</li><li>ESP32：负责程序逻辑、按键处理、屏幕显示、存储、UI 界面、控制流程等。</li><li>SI4732：负责真正的收音工作（AM / FM / SW / SSB 的射频接收与解调等）。</li><li>两者之间，通过 I²C 通信，ESP32 向 SI4732 发送控制命令（调频、切台、模式切换等），SI4732 返回状态和信号质量数据。</li></ul></div><h2 id="开发工具"><a href="#开发工具" class="headerlink" title="开发工具"></a>开发工具</h2><ul><li>ESP32 开发的工具链：<ul><li>IDE：Arduino IDE</li><li>SDK：Arduino Core for ESP32</li><li> 硬件：ESP32 开发板 + SI4732 芯片 + 屏幕</li></ul></li></ul><hr><ul><li> Arduino 官方提供的 Arduino Core for ESP32 支持以下芯片：</li></ul><table><thead><tr><th>芯片</th><th> Arduino Core for ESP32 的支持</th><th>说明</th></tr></thead><tbody><tr><td> ESP32</td><td>✅ 完全支持</td><td>最成熟、资料最多</td></tr><tr><td> ESP32-S2</td><td>✅</td><td>USB 原生</td></tr><tr><td> ESP32-S3</td><td>✅ 完全支持</td><td>新一代，常用于 ATS MINI 项目</td></tr><tr><td> ESP32-C3</td><td>✅</td><td>RISC-V</td></tr></tbody></table><ul><li>Arduino Core for ESP32 对比 ESP-IDF（乐鑫官方原生 SDK）</li></ul><table><thead><tr><th>对比</th><th> Arduino Core for ESP32</th><th>ESP-IDF</th></tr></thead><tbody><tr><td> 上手速度</td><td>⭐⭐⭐⭐⭐</td><td>⭐⭐</td></tr><tr><td>ATS MINI 项目适配</td><td>⭐⭐⭐⭐⭐</td><td>❌</td></tr><tr><td>驱动成熟度</td><td>⭐⭐⭐⭐</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>学习成本</td><td>低</td><td>高</td></tr></tbody></table><div class="admonition note"><p class="admonition-title">ESP-IDF 是什么</p><ul><li>ESP-IDF（Espressif IoT Development Framework）是乐鑫官方提供的 ESP32 系列底层开发框架（SDK）。</li><li>ESP-IDF 的特点是官方原生（乐鑫自己用），直接使用 FreeRTOS，多任务控制更细，代码更复杂。</li><li>ESP-IDF 适用于专业、灵活、产品级开发，比如做商业级产品 / 深度定制才会使用 ESP-IDF。</li></ul></div><div class="admonition note"><p class="admonition-title">Arduino IDE + ESP32 是怎么连起来的？</p><ul><li>使用 Arduino IDE + ESP32 进行开发时，使用的硬件仍然是 ESP32 开发板，并不是 Arduino 开发板。Arduino IDE 只是一个开发工具（IDE），用于编写、编译和烧录程序，ESP32 才是真正运行程序的硬件平台。</li><li>Arduino IDE 中使用的 Arduino Core for ESP32 相当于一层 "适配层 / 抽象层"，它基于 ESP-IDF 实现，将 ESP32 的底层能力封装为 Arduino 风格的 API，使开发者可以通过熟悉的 Arduino API 来开发 ESP32，而无需直接接触 ESP-IDF。</li><li>在 Arduino IDE 里，选的是 ESP32 开发板，写的是 Arduino API 风格代码，跑的是 ESP32 开发板（芯片）。</li><li>Arduino IDE 完全支持 ESP32 开发板，而且 ATS MINI、SI4732 项目本身就是基于 Arduino 体系开发的。</li></ul></div><h2 id="入门步骤"><a href="#入门步骤" class="headerlink" title="入门步骤"></a>入门步骤</h2><ul><li><p>第一步：搭建环境，跑通示例</p><ul><li>行动：安装 Arduino IDE，添加 ESP32 开发板支持；导入 ATS MINI 的源代码，尝试在电脑上成功编译。</li><li>学习点：嵌入式开发的第一步就是工具链的搭建，这能让你熟悉最基本的流程。</li></ul></li><li><p>第二步：购买开发板，点亮第一盏灯</p><ul><li>行动：购买一块 ESP32 开发板（如 ESP32-DevKitC），不接 SI4732，先学习如何控制一个 LED 闪烁，读取一个按键。</li><li>学习点：这是嵌入式开发的 <code>Hello World</code>，让你建立 “编写代码 -&gt; 硬件响应” 的直接概念，消除对硬件的陌生感。</li></ul></li><li><p>第三步：连接核心部件，理解通信</p><ul><li>行动：将 SI4732 模块通过 I2C 接口连接到 ESP32。不要急于用 ATS MINI 完整代码，而是先寻找 SI4732 的 Arduino 基础库，尝试写几行代码，让 ESP32 命令 SI4732 收一个已知的 FM 电台，并读出信号强度。</li><li>学习点：这是项目的核心，你将学会如何通过查阅芯片数据手册和使用现有驱动库来控制关键器件。I2C 通信是嵌入式开发中最常见的技能之一。</li></ul></li><li><p>第四步：深入研究 ATS MINI 源码</p><ul><li>行动：在完成了前三步的基础上，再打开 ATS MINI 的工程。此时你看代码的心态会完全不同。你能找到初始化 SI4732 的代码、扫描频段的函数、处理编码器输入的逻辑。</li><li>学习点：从 “读天书” 变成 “看图说话”。你能结合自己的实践经验，理解开源项目的架构设计，并知道在哪里修改以实现自己的功能。</li></ul></li></ul><div class="admonition note"><p class="admonition-title">学习顺序推荐</p><ul><li>对于 ESP32 + SI4732 + ATS MINI 开源收音机项目，"在实践中遇到问题，然后针对性地学习理论" 是最高效的方法。推荐的学习顺序如下：</li><li>购买硬件 -&gt; 搭建开发环境 -&gt; 运行 Demo -&gt; 修改小功能 -&gt; 遇到具体问题（如：I2C 通信失败、显示乱码）-&gt; 搜索问题、学习相关知识 -&gt; 解决问题 -&gt; 继续迭代。</li></ul></div><h2 id="学习资源"><a href="#学习资源" class="headerlink" title="学习资源"></a>学习资源</h2><ul><li>ESP32：乐鑫官方文档是最好资源，重点关注 Arduino Core for ESP32 部分。</li><li>SI4732：Silicon Labs 官方的 AN383（应用笔记） 和 SI4732 数据手册是必读文档，它详细说明了所有命令。</li><li>ATS MINI 项目：仔细阅读其 GitHub 仓库的 README 和 Wiki（如果有），这里面通常包含了硬件连接图、编译说明和常见问题。</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="/posts/32f038a6.html">开源收音机开发技术调研</a></li><li><a href="https://esp32-si4732.github.io/ats-mini/hardware.html">esp32-si4732/ats-mini 官方文档</a></li></ul>]]></content>
    
    
    <summary type="html">基于 ESP32、SI4732 开发收音机。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>开源收音机开发技术调研</title>
    <link href="https://www.techgrow.cn/posts/32f038a6.html"/>
    <id>https://www.techgrow.cn/posts/32f038a6.html</id>
    <published>2026-01-09T13:03:50.000Z</published>
    <updated>2026-01-09T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="技术路线"><a href="#技术路线" class="headerlink" title="技术路线"></a>技术路线</h2><p>为了开发一款 DIY 收音机，非嵌入式开发者通常可以选择以下任意一种技术路线。</p><h3 id="三种技术路线"><a href="#三种技术路线" class="headerlink" title="三种技术路线"></a>三种技术路线</h3><ul><li>技术路线一：模块化开发（推荐起点）<ul><li>怎么做：购买调频（FM）收音机模块（如 RDA5807、TEA5767）或网络收音机模块，用 Arduino 或 ESP32 这类开发板通过 I2C 等简单接口控制，甚至可以用树莓派做网络收音机。</li><li>学习重点：基础的电子连接、Arduino IDE 的使用、调用现成的库函数。</li><li>优点：最快出成果，能建立信心，适合所有初学者。</li></ul></li></ul><span id="more"></span><ul><li><p>技术路线二：基于开源项目二次开发（平衡学习与成果）</p><ul><li>怎么做：在 GitHub 等平台找到开源收音机项目（例如，搜索 “Open Source Radio ESP32”）。下载其原理图、PCB 文件、源代码进行研究、焊接、烧录，并尝试修改功能（如增加显示屏、改变 UI）。</li><li>学习重点：阅读和理解他人代码、学习使用 PCB 设计软件（如 KiCad）、掌握基本的焊接和调试技能。</li><li>优点：在真实项目中高效学习，是成为合格开发者的最佳途径。</li></ul></li><li><p>技术路线三：从零开始（不推荐新手直接尝试）</p><ul><li>涉及内容：从芯片 DataSheet 开始设计收音机高频头、中放电路；为微控制器编写底层寄存器代码驱动音频解码芯片；实现复杂的用户界面和网络协议栈。</li><li>挑战：需要深厚的模拟 / 射频电路知识和嵌入式系统开发能力，极易因调试困难导致项目流产。</li></ul></li></ul><h3 id="技术路线推荐"><a href="#技术路线推荐" class="headerlink" title="技术路线推荐"></a>技术路线推荐</h3><p>对于绝大多数个人开发者，强烈推荐从路线一或者路线二开始。<strong>尤其是路线二，它让你在一个高起点上站上巨人的肩膀，既能做出有成就感的作品，又能通过实践快速学习嵌入式开发中最实用的部分。</strong>一个有效的行动思路是：</p><ul><li>(1) 明确核心：先想清楚最想要的是 “一台自己做的收音机”，还是 “深入掌握嵌入式开发全过程”。如果是前者，绝对不要从零开始。</li><li>(2) 寻找项目：立刻去 GitHub 等平台，用 “open source radio pcb” 或 “arduino radio” 等关键词搜索，找一个 star 数多、文档齐全的项目。</li><li>(3) 动手复刻：按照项目指导，购买现成的 PCB（或打样）、采购元件、焊接、烧录程序，这个过程本身就是最好的学习。</li><li>(4) 迭代升级：成功运行后，再尝试修改代码、增加功能，逐步深入到嵌入式开发中。</li></ul><h3 id="技术路线总结"><a href="#技术路线总结" class="headerlink" title="技术路线总结"></a>技术路线总结</h3><table><thead><tr><th>项目目标</th><th>推荐技术路线</th><th>嵌入式开发需求</th><th>难度评估</th><th>核心工作重心</th></tr></thead><tbody><tr><td>快速实现一个能用的收音机，验证想法</td><td>使用现成模块 / 开发板（如 ESP32 + 网络模块）</td><td>极低。主要进行拼接和配置，类似搭积木。</td><td>★☆☆☆☆（入门）</td><td>软件配置、外壳设计、系统集成</td></tr><tr><td>制作一个功能完整、有学习价值的收音机</td><td>基于成熟开源项目进行修改和优化</td><td>中等。需要理解、修改现有代码和硬件。</td><td>★★☆☆☆（进阶）</td><td>代码阅读与调试、电路调试、功能增删</td></tr><tr><td>从晶体管 / 芯片级打造，追求极致控制或学术研究</td><td>从零设计电路并编写底层驱动</td><td>极高。需精通电路设计、MCU 架构、通信协议、嵌入式 C 语言。</td><td>★★★★★（专业）</td><td>芯片选型、原理图 / PCB 设计、固件开发</td></tr></tbody></table><h2 id="开源固件项目"><a href="#开源固件项目" class="headerlink" title="开源固件项目"></a>开源固件项目</h2><div class="admonition note"><p class="admonition-title">提示</p><p>在 GitHub、Gitee 等代码托管平台，使用 <code>SI4732</code>、<code>ESP32</code>、<code>mini radio</code> 等关键词组合搜索，能找到更多优秀的开源收音机项目。</p></div><h3 id="ATS-MINI"><a href="#ATS-MINI" class="headerlink" title="ATS MINI"></a>ATS MINI</h3><h4 id="项目简单介绍"><a href="#项目简单介绍" class="headerlink" title="项目简单介绍"></a>项目简单介绍</h4><ul><li>在开源硬件与软件领域，ATS MINI 项目无疑是一个值得关注的技术瑰宝。它是一款专为 SI4732（ESP32-S3）Mini/Pocket Receiver 设计的固件，旨在为用户提供一个高度集成、易于使用的收音机解决方案。</li><li>ATS MINI 项目的核心是一个功能齐全的收音机固件，它基于多个开源项目的经验与智慧，例如 Volos Projects、PU2CLR、Ralph Xavier、Goshante 以及 G8PTN 等。这些开源项目的合并，使得 ATS MINI 在性能与稳定性上有着坚实的基础。</li></ul><h4 id="项目主要特点"><a href="#项目主要特点" class="headerlink" title="项目主要特点"></a>项目主要特点</h4><ul><li>高度集成<ul><li> ATS MINI 项目将 ESP32-S3 与 SI4732 模块的优势高度集成，用户无需担心复杂的硬件兼容性问题，只需关注于功能的实现和优化。</li></ul></li><li>灵活配置<ul><li>项目支持多种硬件配置，用户可以根据自己的需求自由搭配，实现个性化定制。</li></ul></li><li>开源共享<ul><li> ATS MINI 项目遵循开源协议，用户可以自由使用、修改和分发，促进了技术的交流与共享。</li></ul></li><li>完善文档<ul><li>项目提供了详细的硬件、软件以及刷机文档，即使是新手也能快速上手，降低了使用门槛。</li></ul></li></ul><h4 id="项目技术分析"><a href="#项目技术分析" class="headerlink" title="项目技术分析"></a>项目技术分析</h4><p>ATS MINI 项目采用 ESP32-S3 芯片作为主控制器，结合 SI4732 收音机模块，实现了 AM/FM/DAB+ 等多种广播信号的接收。其技术亮点如下：</p><ul><li>高性能处理器：ESP32-S3 提供了强大的处理能力和丰富的接口，使得收音机在处理信号和用户交互时游刃有余。</li><li>模块化设计：项目支持多种硬件配置，用户可以根据自己的需求选择不同的硬件模块，实现个性化定制。</li><li>丰富接口支持：ESP32-S3 的丰富接口使得 ATS MINI 能够支持屏幕显示、按键输入等多种交互方式。</li></ul><h4 id="项目应用场景"><a href="#项目应用场景" class="headerlink" title="项目应用场景"></a>项目应用场景</h4><p>ATS MINI 项目不仅适用于个人爱好者进行 DIY 创作，也适用于教育、科普等领域。以下是几个典型的应用场景：</p><ul><li>个人娱乐：用户可以自定义收音机的外观和功能，打造独一无二的个人娱乐设备。</li><li>教育工具：ATS MINI 可以作为一个教学工具，帮助学生了解无线信号传输原理，提高实践操作能力。</li><li>科普展示：在科技馆、展览馆等场所，ATS MINI 可以作为展示无线信号传输技术的展品，供参观者体验。</li></ul><h4 id="项目开源地址"><a href="#项目开源地址" class="headerlink" title="项目开源地址"></a>项目开源地址</h4><h5 id="上游开源项目"><a href="#上游开源项目" class="headerlink" title="上游开源项目"></a>上游开源项目</h5><table><thead><tr><th>名称</th><th> GitHub 地址</th><th>描述</th></tr></thead><tbody><tr><td> Volos Projects — TEmbedFMRadio</td><td><a href="https://github.com/VolosR/TEmbedFMRadio">https://github.com/VolosR/TEmbedFMRadio</a></td><td> 一个围绕 SI473x 系列芯片的嵌入式 FM 收音机示例项目，主要实现基础 FM 收音功能，是多个 ATS MINI 及相关固件项目参考和借鉴的早期实现之一。</td></tr><tr><td>PU2CLR SI4735 Library for Arduino</td><td><a href="https://github.com/pu2clr/SI4735">https://github.com/pu2clr/SI4735</a></td><td> 面向 SI473x 系列收音芯片的 Arduino 库与 Radio 实现，支持 AM、FM、SSB 等多种模式，接口设计清晰，是 SI473x 生态中被广泛引用的基础项目。</td></tr><tr><td>Goshante — ats20_ats_ex</td><td><a href="https://github.com/goshante/ats20_ats_ex">https://github.com/goshante/ats20_ats_ex</a></td><td> 面向 ATS-20 接收机的增强固件项目，基于 SI4732 / SI4735 芯片，对界面和功能进行了扩展，是 ATS MINI 系列固件的重要参考来源之一。</td></tr></tbody></table><h5 id="下游开源项目"><a href="#下游开源项目" class="headerlink" title="下游开源项目"></a>下游开源项目</h5><h6 id="调频收音机"><a href="#调频收音机" class="headerlink" title="调频收音机"></a>调频收音机</h6><ul><li><p>ATS MINI 原始版本 / 老版本固件：<a href="https://github.com/G8PTN/ATS_MINI">G8PTN/ATS_MINI</a></p><ul><li><code>G8PTN/ATS_MINI</code> 是最早的原始 ATS MINI 固件来源之一。</li><li>这是由作者 <em>Dave (G8PTN)</em> 发布的最初或早期的 ATS MINI 固件代码，围绕 ESP32-S3 + SI4732 搭建的固件实现。</li><li>该项目包含基础的功能、菜单扩展、电池电压显示等，可作为项目的一个核心实现版本。</li></ul></li><li><p>ATS MINI 社区 Fork 版本 / 发展版本固件：<a href="https://github.com/esp32-si4732/ats-mini">esp32-si4732/ats-mini</a></p><ul><li><code>esp32-si4732/ats-mini</code> 是一个 “ATS MINI Firmware Fork（ATS MINI 固件派生版）”，基于包括 <code>G8PTN/ATS_MINI</code> 在内的多个开源项目来开发，例如 Volos Projects、PU2CLR、Ralph Xavier、Goshante 以及 G8PTN 自己的 ATS_MINI。</li><li>该项目围绕 ESP32-S3 + SI4732 搭建的固件实现，它集成了更多贡献者的改善、修复、特性扩展和新功能，代码更活跃、更新频率更高，并且形成了自己的版本体系（比如 <code>v2.x</code> 系列）。</li></ul></li><li><p>SI4732 Radio 固件：<a href="https://github.com/joaquimorg/si4732-radio">si4732-radio</a></p><ul><li><code>si4732-radio</code> 是基于 <code>G8PTN/ATS_MINI</code> 的派生实现，偏向个人维护 / 定制化方向，围绕 <strong>ESP32-S2</strong> + SI4732 搭建的固件实现。</li><li>该项目由 <em>Joaquim Org</em> 维护，定位更偏向 通用型 / 工程示例型固件，适合作为 SI4732 芯片的软件参考实现与二次开发基础。</li></ul></li><li><p>SI4732 / SI4735 Radio 固件：<a href="https://github.com/ralphxavier/SI4735">ralphxavier/SI4735</a></p><ul><li><code>ralphxavier/SI4735</code> 是一个面向 SI4732 / SI4735 收音芯片的开源固件与示例项目，旨在为基于这些芯片的收音机开发提供功能实现和驱动参考。</li><li>该项目支持在 ESP32 系列 MCU（包括 ESP32-S2 / ESP32-S3）平台上运行，通过 I²C 接口控制 SI4732 / SI4735，实现 FM、AM、SW 等广播波段的接收与基础操作，并提供基本界面交互示例代码。</li><li>项目主要特点包括对硬件模块的收音控制逻辑封装、示例功能展示及对常见显示与按键硬件的支持，是一个适合作为 SI473x 系列芯片学习、移植与二次开发的基础实现仓库。</li></ul></li><li><p>SI4732 Mini Radio 资源汇总：<a href="https://github.com/Ramsin/SI4732-Mini-Radio-Files">Ramsin/SI4732-Mini-Radio-Files</a></p><ul><li>SI4732 Mini Radio 的固件代码、硬件设计（PCB）、资源链接等汇总。</li></ul></li></ul><h6 id="网络收音机"><a href="#网络收音机" class="headerlink" title="网络收音机"></a>网络收音机</h6><ul><li><p>yoRadio / 网络收音机固件: <a href="https://github.com/e2002/yoradio">e2002/yoradio</a></p><ul><li><code>e2002/yoradio</code> 是一个基于 ESP32 + I2S DAC 或 VS1053 音频解码模块的网络互联网电台播放器固件项目，用于构建可播放网络电台流（Web Radio）的硬件网络收音机，不支持 FM 调频，完全依赖 Wi-Fi 网络流媒体。</li><li>该项目定位为高度可定制的通用网络收音机固件，支持多种 ESP32 开发板和音频输出方案，适合 DIY 桌面网络收音机、智能音箱类设备。</li><li>功能特点包括：<ul><li>支持 MP3、AAC 等常见网络电台流格式，通过 I2S DAC（如 PCM5102、ES9023 等）或 VS1053 输出音频。</li><li>支持多种显示屏（如 SSD1306、ST7735、ILI9341 等），可显示电台名称、播放状态、时间、菜单等信息。</li><li>内置 Web 管理界面，可通过浏览器配置 Wi-Fi、电台列表、系统参数，无需反复刷固件。</li><li>支持多种人机交互方式：按键、旋转编码器、触摸屏等，硬件适配性强。</li><li>支持 OTA 在线升级、电台列表导入导出、配置持久化存储。</li><li>可选支持 MQTT / Home Assistant 集成，适合智能家居场景使用。</li></ul></li></ul></li><li><p>ESP32 Radio Evo3 / 网络收音机固件: <a href="https://github.com/dzikakuna/ESP32_radio_evo3">dzikakuna/ESP32_radio_evo3</a></p><ul><li><code>dzikakuna/ESP32_radio_evo3</code> 是一个基于 ESP32-S3 + PCM5102A 音频 DAC 的网络互联网电台播放器项目，称为 Evo (Evolution 3)，主要用于构建可播放全球在线电台流的硬件网络无线电设备，不支持 FM 调频，完全依赖 Wi-Fi 网络流媒体。</li><li>该项目支持 MP3、AAC、VORBIS 和 FLAC（最高 1.5 Mbit/s）等多种流媒体格式，并集成了 OLED 显示、旋转编码器、IR 遥控等交互控制功能，让用户能方便进行调台、调音量等操作。</li><li>功能特点包括：<ul><li>从预设的 “电台库” 文件获取网络电台流（支持 SD 卡或 GitHub 下载）。</li><li>使用 I2S 与 PCM5102A DAC 解码音频并输出。</li><li>内置完整的 Web 服务器，可通过浏览器在电脑或手机上控制电台、音量等设置。</li><li>OLED 显示屏显示当前电台信息、音量、VU 表等。</li><li>使用单旋转编码器实现菜单操作（包括音量、台号、库切换等），同时支持 NEC 标准红外遥控器操作。</li><li>支持 OTA（网页直接更新）、SD 卡浏览、3 种显示模式、睡眠定时器、3 点均衡器 等扩展功能。</li></ul></li></ul></li></ul><h6 id="二合一收音机"><a href="#二合一收音机" class="headerlink" title="二合一收音机"></a>二合一收音机</h6><h2 id="开源硬件资源"><a href="#开源硬件资源" class="headerlink" title="开源硬件资源"></a>开源硬件资源</h2><h3 id="硬件博客资源"><a href="#硬件博客资源" class="headerlink" title="硬件博客资源"></a>硬件博客资源</h3><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th></tr></thead><tbody><tr><td> esp32-si4732/ats-mini Documentation</td><td><a href="https://esp32-si4732.github.io/ats-mini/hardware.html">https://esp32-si4732.github.io/ats-mini/hardware.html</a></td><td><code>esp32-si4732/ats-mini</code> 项目的官方文档，包含硬件架构说明、引脚定义、参考设计等相关硬件资料。</td></tr><tr><td>Xtronic - ESP32 S3 SI4732 Pocket Multiband Receiver</td><td><a href="https://xtronic.org/circuit/rf/radio-receiver/esp32-s3-si4732-pocket-multiband-receiver/">https://xtronic.org/circuit/rf/radio-receiver/esp32-s3-si4732-pocket-multiband-receiver/</a></td><td>Xtronic 网站上的硬件资料，提供 ESP32 S3 SI4732 口袋多波段接收机相关电路说明、部件清单和设计参考。</td></tr><tr><td>PCBWay - ESP32 S3 SI4732 Pocket Multiband Radio Receiver</td><td><a href="https://www.pcbway.com/project/shareproject/ESP32_S3_SI4732_pocket_multiband_radio_receiver_40ce2b94.html">https://www.pcbway.com/project/shareproject/ESP32_S3_SI4732_pocket_multiband_radio_receiver_40ce2b94.html</a></td><td>PCBWay 社区分享的 ESP32 S3 SI4732 项目，展示 PCB 设计截图、硬件资料及项目概览，可用于了解板子外观与布局。</td></tr><tr><td>MakerWorld - SI4732 Project</td><td><a href="https://makerworld.com/en/models/785921">https://makerworld.com/en/models/785921</a></td><td>MakerWorld 上的 SI4732 项目模型，包含 3D 打印视图及硬件模型，有助于从机械和装配角度理解项目结构。</td></tr></tbody></table><h3 id="立创硬件资源"><a href="#立创硬件资源" class="headerlink" title="立创硬件资源"></a>立创硬件资源</h3><h4 id="调频收音机-1"><a href="#调频收音机-1" class="headerlink" title="调频收音机"></a>调频收音机</h4><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th><th>开源固件源码</th></tr></thead><tbody><tr><td> OSHWHub - ESP32S3 + SI4732 多波段收音机 </td><td><a href="https://oshwhub.com/sunnygold/esp32s3-si4732-shou-yin-ji">sunnygold/esp32s3-si4732-shou-yin-ji</a></td><td> 基于 ESP32-S3 + SI4732，提供完整的原理图、PCB、BOM 等可下载资源，适合 DIY 和二次开发。</td><td><a href="https://github.com/ralphxavier/SI4735">ralphxavier/SI4735</a></td></tr></tbody></table><h4 id="网络收音机-1"><a href="#网络收音机-1" class="headerlink" title="网络收音机"></a>网络收音机</h4><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th><th>开源固件源码</th></tr></thead><tbody><tr><td> OSHWHub - ESP32 + Wi-Fi 网络收音机 </td><td><a href="https://oshwhub.com/twist_66/i-can-listen-to-the-undamaged-in">twist_66/i-can-listen-to-the-undamaged-in</a></td><td> 基于 ESP32 + PCM5102 的网络播放器开源项目，支持蓝牙、流媒体网络电台，可播放 FLAC / AAC / MP3 等格式，通过 Web 页面进行控制，兼具实用性与可玩性。</td><td><a href="https://github.com/dzikakuna/ESP32_radio_evo3">dzikakuna/ESP32_radio_evo3</a></td></tr><tr><td>OSHWHub - ESP32 + I2S DAC 网络收音机 </td><td><a href="https://oshwhub.com/hudiesudie/chao-ji-jian-dan-ban-wang-luo-shou-yin-ji-2025-nian-1-yue-6-ri-ban">hodiesudie / 超级简单版网络收音机</a></td><td>极简 ESP32 网络收音机开源硬件项目，侧重 “低成本 + 易制作”，通常搭配 I2S DAC 使用，用于播放网络电台流，不包含 FM/AM 硬件。</td><td><a href="https://github.com/e2002/yoradio">yoradio</a></td></tr></tbody></table><h4 id="二合一收音机-1"><a href="#二合一收音机-1" class="headerlink" title="二合一收音机"></a>二合一收音机</h4><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th><th>开源固件源码</th></tr></thead><tbody><tr><td> OSHWHub - ESP32S3 + SI4732 多波段 + 网络二合一收音机 </td><td><a href="https://oshwhub.com/abcdef2022/full-band-network-2-in--1-radio">abcdef2022/full-band-network-2-in–1-radio</a></td><td> 在传统 SI4732 多波段收音机基础上，增加了 <strong>WiFi 网络功能</strong>，提供原理图、PCB、BOM 等完整硬件资料。其 v2.2 版本增加了网络收音机解码和 PCM5102 音频部分等改进。</td><td></td></tr><tr><td>OSHWHub - ESP32S3 + SI4732 多波段 + 网络二合一收音机 </td><td><a href="https://oshwhub.com/yaobaling/yoradio-radio">yaobaling/yoradio-radio</a></td><td> 基于 ESP32-S3 和 SI4732 的开源多波段 / 网络收音机项目，在 SI4732 多波段收音机设计上增加了耳放和网络收音机相关元件，并适配了网络收音机功能（固件需另行配合）。</td><td></td></tr></tbody></table>]]></content>
    
    
    <summary type="html">开源收音机开发技术调研。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>近期开发计划</title>
    <link href="https://www.techgrow.cn/posts/29d74d90.html"/>
    <id>https://www.techgrow.cn/posts/29d74d90.html</id>
    <published>2026-01-03T14:38:21.000Z</published>
    <updated>2026-01-03T14:38:21.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误, 请重新输入." data-whm="文章校验失败, 但不影响阅读解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="7aa038b85c2e84e72ab1db689533070f5573ed05f84a9c955d7977b52ef5538e">4cfab7e4fdfd9afdbf1592f9652b8a199f3cb3b67d053a3a2b7db0e886465ca1ea14179f28e40c4d184809b24d2f909058d2606747febc969140bf1db3dbbb1a7542b98eb3d7de26ec2c86ec81828b35913853b3ca6e01e7cdd106a9462dcbff705b54ac505b28447538b61c3ec4400b8cb84079a99816fe59006d71ef364f10dfb4d6a52fa3c40030b50b35a957f58eeaf772e9c44236a3270411d3be05e2b07d30b9bac2b2c9e4a597c4d551dde647547269d613d69e329a68cd1dcba6a2bae36bd33e5e11d2a5d518899e43e6fbe0524ed9b9c309564739dcbd3851cc07f0cf45fdf977054a7183a76498682d3bcbe26762ba095458de76d41fd18e8353e46f9e6a744945ca97a1e13148bb3f9423d01ee13a6b4834facb1fd3008b9bc0b23815838f409203118497fae9e3c50332fdcc1f781ba863ad73325b42ab0feec4ff52515efcaae7b170dbd491c656284a85ddf836e73fcccf8e7ca5401292a93019511a0678c1a62a4f9d524748fa16eb444d4ba9b9709af6db0fd8358faffb7d0c03a8d01f503646851d8ec0c9f69f5e075714dcc796357f1870f6f0dca899a9bf358860381dd91e102a52d8499852d404fc84364601414cf25f113952a4e8453feca610d559c44f8a12d16bfedd77987b81e1710f415778084a5bb8f4a5c6c2aab5f7ed9a2212789eea24995e31f21d19c0f60d3d46829a35a209ad5a64ba59a81bdcf152f450d14cd1a48d46a1387566700476d2ecdb9d8353d4cbf63e855586f66ae706f65dc4955184b9a65a836128c63f24689bd56daa7a178ce5757d9d01dc84779600b82e6a988dab8b740a61c2ff52336f31143d91d739de64ac0009731caf857040616087028acc4ed0f44ce7db4b06867cc89fbeb38e768587f29842144388051bf5a9d0e6d50c34f302fd7baa802f9f9c168ffda3ebac1d2050694c4497144e66768873510e3bd1c5331c36ce81277ca2b8b28a332997ea2d4a0c033d8734e7f53a93fd0f7d93befa1e2ca440424342540b5fb5dafb58bae19439fc52008da933c4ab1c09c9397180751c729410b4e33e4b42e2ca464123092a013681dc189d3c6c9b568c9ef209dd27aa322816b62fdb4d438cbb725b076f448c90ab1226f482c58e4a439618e5544ad0b035ece5da84f39b6e91cc187ad85bba9ad3d1df678e401029e9de1b70b3eb57536d7dd879dfcf8c3e624a38256e95a99af821624daa673e38bd8747b26b94d5348d0d27779b447f343f87368acb8e537276216e2e5e6561d9ccaa46b82bd680bce7c4f4cc56d82439b2024cb26c26c284a9d8f5ce24f35b52d335f8b62fc257b04b8ebac321d9785d90e835961fd2c723e3eaf1e9e9f98ee5db63e0521cac3e574a777bb5f1b4b5dbb8ba3d93a4168cc2dfcc0b9a0ae2442d97a01fa711ef780b41f63d893f1f9f6e88ed61953c66ef74636546b60e5f3b74dedf7826adb9aafc045a4d081cc0c6c46f7d99ecc3ac20e73e1b55a401ba5aa22f530c892de0f7961c766fc616288952010e6da9b886d01b932b09af54700ca58efd3d810c1b6e44c3d4f523cd5bf484023309b809e90de2882acb79c237a7a53594e54cd663039a5c74d97b93a5552c24ef2db394e2bca465056620ea92087125ac05f012fe376adaad2684a73e829406a0537e0c721990efafb49abc2d2573fef95878899b9b3bc40aebb4dacfc78dedfe5f8c510f95fc8cb0c645e44a85a438a33e3abfae5b91404b5baaa760e533120ae51b8a1b45d03db0bb7de2afeb3c68cd14915e3aa2cf82d4006b4925c5c061e4e67f779c38b0398feb23efe1b4af81aa7c13957cee35779cb2baf9aaef4eb0074ba9856601131144f3f32b06b8576ebaeaea8467d965e248dec371e7458f974280e534d7df0ed9d0bd01e8d8c8699fab51b573418372ce4c097c62b155d034eab1fb62d37e66d1931e99c8c49eff493573b2f81085af4de971a5b6c8798d3aae32abc0058307e4eacb6d4c20a714d8025ef4a7aabca790b41248b65894b64b5844136de8c10b9a51a96137dfdae9fbe2f8aa1bec41ec1b99bd9e00772946d236131f0e967f002172ee52b95cd5294ee434c95d941684c440616e2d692c46ce12482b1b975fea4822b6c403a9044b94723fb0f8c340d69c4280cdc99c7ad5bfd27a961d195ab077396fb346fbf60a83f70fd162efc7dea1b6b0aab01b08a18fd862d646ecee92cc5b4aac63b70a87e5374a6b2ad3732c63be3b7fec49225659ec8661145100469d3368056e1c5059b25129d28f297492af8aa0d955560b368ccaf6da9b4b74726223aa8713bc5d07c0683dec0ecdd18c1eb5758f6c9fb7f2b378bb0f0e9158c5b1f4e9965ea2dd715b871e0a4b17b315e9212509bb190ba53daefac614109d530a6c6a72a403193252f64cde247028c19e65b05e2aa5a628efed935c6ac9c288ea9b946bebbcfea053b7abf03b3fa9a8bbf594f4a364281fdddbb6404a71011f4bf85ad4eb9042d591b74b9b827ef705ea7fb47c6503cba8dd79b7d1a33e8e3a15bb2d8e2ca8a36eb685c94b2d78215b71c98b2c0b3ee37e035bafbbafef30c0983a823efe8e7117435dd296db8e79ee3f3a744a2538dfde388f61d0a6d130734c4e44be686c870afdb7a1caa863bdabc994fce857ebf7972073d4426ebf9405f5926b4a25db7461119b55bd9ec4bd07931eb8c7d447e022e17a2df094a1dc00dcacd1fc064f1054276eed105893619605c48d576be245c5b87c97301e98a03f4e7214bb91ccb684917f6d96f2fd3488a7eca5ee845751e15fa94e41092d5e55efeab282dcdd9e11323b8a3946b92b5a59100d1e1491e329296363557a9af9262b745f52b40442e617efb7736c99a740dca8af13ebddcd673eb4a534f72e933973592758b63bbd367081a651b5caa7693a2a0ebda3c9f381a2ba519351c4c1d716001a1848bdd3202c31f564397ceb9eb3e4e70041673ccfaa66bd757cea040a014a4391e18d126faec37a2ee964edd824d8c5d2de2c3ead3f410961b608cad747e2bc14687da33756d6de87cc5f8dff3547d7e63964d64eafa0cb410cf2690800747f38b1a7921cfb554ea6bfff93e4207b42461762ef62c1bdc669e1899d1bb55eacf78d9996258735b9a96d7632068e0736297bda947ac28572609247d9f0e1c88c57b891ae7e97a36b4332161e91c23a37f07780aeb2ec85d85e9dea91110a395c7b162dcc3995b684d10eb28d8c55aa78a45b50171f6b23658357003d77d76c00f69027d812e5a3278649dd7f510af0d5eb652abdc305daab3743709e2dad44b171ddc5be2200541a923d4fb3b83785916e92604a89b9f17fc4b838abb234a80e9e037601fc00ef31188b3464ae33435dfb10ab5c973090ac8f01badd332ec2e904e605cc4a3385be2648132d8718fa5f8fb37c035144517ab367a704133c9644c5d1dbcccd2cb46bf88d63527a0116c8282a3f90f9c7a142d29ee89e95e272aa7063df9143ab4ddba067e8d9fb8e84e1ed83e648e0d238ee55f89ac10c0d62477d0f7c123e9c3d88d6e94e2fdc0b162ac345a16e8a23a0c02f82b4e358a37eca0f550842074acdf047d912df3390745af62d280f49d6292bf2f2c09c0a824f6b8636cdb1638a362e2652aea98475ad25b689189ed0940d539736cadb54cb971531dd44ccb04dc1ae651569af62e117d836931dca4e0895fd6eeb4bca0d59f7c421dbe4e44b4cb69b59b67356219451c11b8d7dd5bf20e013a1ffe68d983d1f722c28bc1d7919d49391c48bf1f7ad94a8950d4cc574db236fdd0bae29273aff2931b6b0b981d3e8b6031c1e6f29a5bc4bbd788723eb8b77b8d311fbf17f848d7709988df6130a9050950aa3dea53fee05c55ce0129f5d50dc4bb80243fd3dcfd8ea416b51808c78d2b2a48f9295cb0c48d850f4d9ea9754f770976bcf621dd13892580768a72d15472a8cdc8025628dfbd380c5be7a4f9d36dc72cc0a5a5281119618c23394885a86b6e0fcf909f2fe8964b41f729a21ea6fe351b1057ae08b3e1afc0d3ef3e0a8e6ea83f57ffcf9269dcd4e5a25369630477074a1243e17561171b83664124025196b8581d3daacf8bf6630fe9bc0026fd074b6661e53781f90a757827dff0df9294f141d088c3209794d875c4fac17e0d476f8a760608d272c6e44cb59ba224e27b2df0bf92d167b9c88d7654110876536879bd3fff1db399662dbbe65bb8451b63dfefba2c0cc6511f4d0bd7135cd679d571b0f8278fb6d16883a843ac8feef8a31fe0484d3a70ae7e9e264437e831c618323b273e75b07cc94a4942b7abee0e629f1d1afefdff8bb8d1e92b9d256a9b8270f7ed297bae1cecd45422e80e518f7911d1a0fd78b969d016e08d330fa115d88ce949c137b9cd4f094428fdb499bf6d554a1bf5a89e15cff3a2ca75b42c4e45593dec554558a68993f38206a93ed9f0108dda3554077915127d450d5517f8b63e2274614f61f8df13b408a990f9946d710005fe09a1ee277c47cf38c4358e2d3263791a611a975c8d10ab60b427b5cb5a8551f564bbf59daab665729f836398fdf8dead68c8a2f6c540a3b70fbac21f8730386c18d0e24212731760555c6f60b23e4bc8054b59868ff124046064aedb65b38d9c17f7cffe6efe3c9dbc79f95771675913101fecb789a17a362750f9d1d3f23d0293554e9a5364a3f57a57f33924e50a836a1e448f0074c7c1d99a42bfb695109fdd3462da7fe1c0f953a6028ba576dc5f0bd3b8b69782888a326b4c23d48d9ef531ff8a74cc96f7ae17b926bb76e45598d4f4d186485d107985561cbc8f7eb5361ea6ea16dabfa2506c0bf67b316d547735432ae0c1d549c359d932b3866f0d366c9be1ded3c3073af4943144b8f8f5a81a51e9ed6a812b6bfe57c9288a01360741989c98f4a5f814da3bf74171fa964c630020d35fe88e46467f9dd37c0bcdd9c54ad21b5630df463bcffdc9b5086439fbd90cd5feebd21fbf19858a0bff3f959cf4c8aa546625becce865924cbca2047d82cb3451a2054d8896936256978eab4d6708896210809c05f87d4a1f6412ea838e6a22ae451e61c7a0cc0d15dca8897b8c04e8a8f9555ab8c26f8bea71412c44ee056cb8b1be5878d616b5127f581ea01974ac9e8765f48a3d2291685f878cfe2c710c1224aea84f4544b60fe75e20747ea69d919e5438ee2c46f09a3a829a33f8d344cdba5a0d4f8a6be912ee9dc2073e64ca73bb6cf8a18df311c05c8e1cadff10de8bf4284b4195c05729b5daa7d27077042fc43e20b4aa2271d673cfab972be594036dd6add32bfdd752066ba423c74fc701ef431b52eef8908df6526566f8f3e9662dc00f198ee669d37676b2a538b5ca4299f8e982cae65320161f6e43f38a25115091bed6696b1f2d7b2cc5e0199ffb5dbee9b63f219e1ba13da38735677e30cf4336cd8656efac6dc6afade3773ba1104d218d8f3b2020835336c8d7ac1e15e5ce10dc5811c6f52efb493fc5e4dc69304e44b4f753d9e3beebe013acea970a32144a91e53f2049b786e68389162b6337a4bb41e367c15389458e1af8ac7da2d943bd7ca97c8a3b17ce7538b095f15a86b6159e68709b65e196b5d4b44cf844bc6176a4b8ef844464f3987a211a93e24dfa199f8f34b19166e626cce8c8e9dbc116b0fcb62c4cd88b4aeeb0119b76eb38f624cd9b8a1cbfd5f43214f8f45034aeed30c89435f44bdfcd2adf832089f9716f02808677410dad61df3da105822adec82a11bf601a2b14289cbf8fce26b01ae6802dfcd8d0592027b83135734e4147bb453814b708231a72d6d12a58022dc7c8e546fc90f95b6102c5d650925e9613efa2b7a520ffb62bc526cd6602b6d30178ca55c18efc7b058c64d9c3f199390f507c18429710b5d52cf5a3fc655e2eb7688c11df912756dd978dd262293f702824caf505fcd6e551ceef8282d230e2768a63457ecadd3a7f8597dbf914ac64e1235003b652037d5045dddec13e1e31192cbd3072744e593e4f2bb49b136ada88e09ba5576ce8d38e8335f8f4f3290dd837de1c8a51e3acb0877936f072366c1151dc70033c28816f6e9a6efc83ad3425f88f78d62ce100cb5a7723f24374567f5653c46cb8323c69571aa468834974110f5dc82a30875bab673be32e95062212ff48ee5e856672f35710fcefcf29362ea983f223d5ca3d01789ff6450a19f93a521acdb652884c351c56f222a03e9c49e45319c9942d717c30101654f3c51031bb677fbcd5a7a4355c62ccb933b7ce3c3611dfb12234b152bcc270d5d3a57f6ce3bfa9a910f4ff1e2799dcfea466e5ac7f9365acc05ac0c2e9edfa3d0b8f084b5f402dbc4f9941cdbcb8ccf46e1776333e5a2bb6df995be4e3b87f10ec5bb4f04c538d087fc9f5dbc9eb683f1971e53016afc07fa5ecaf402068e19b7b2029215d7b8b2defef4a172968a7ab3dee2cb19cef50825e0e915ebd73dbec0cce52d1944e6aad535af0957f425d5ffe17d51638f241ff13bebb8f1358333bd5f1c9e9d9a560611cfd917e14b53e2a5d653980f581504b29f6ead070e64230ab9dae5410176b8744855e1c165235bffa9b3460dc763afca48d7c41aa892fcf38cf6b3d20ba7a20dad3f604894d404d83e74dcae2f9144843c1a81d6767f55ad82c55b3556d81301baf67529a1de8b33b18822c2a7338059682b196ec7eba1a3baf268ec6869f127437ee6d6ef4a5c518b40523e0674e0ffbf1917dc5ca27deeef6d41a6600b1193e0f0ebe6bd93d6b1bea9943617596c4726e9d9f3aa7cbab91c5a9fc9049c84b4bb9ea41d0c6f1cdfe3f72b1a03ce13344e61e9f8b1f19311d9a7d907243ed69be76f6331fa81f69b09b7533748d7b2a05c9f1474e014c06436d960c68263dd2b6901762151d628e06526bf38fda01d348277169cca7394c10737bce7df1a1336c517683f6a905f0e2bf5e0f9bdea98c4ac933345fc74560d88c4ecd2f4776ffec9b599d5b4366a2dcbf9e8ba78fd2f258141e900ad8f3de5c491595fedb26fd1a4fd4bc88de3735c0390ab8bfd211cde9f6575fdfe227bd67e9cff80241f75cd27bf2d549bb4c03d5887becd8345bee94b40770f09678175698a3d67bf8afaeecdcd2ac2a7e1a9db5fdc81b837cf76b964fdca7c35e527a41f3c32be359c4ad173df3085b78986b28100865a760d7ff0fb80d93eb470a57e9a1667d64c576da0898479cc8f54b74b2c895289fc321081df00400a686d75a116754ea4cb29ce5b6e5917cd7db174df72c4f7cc5ceca1e0b53cdce9779ad6bd511c31e7d5bb73b8f0cb2452b0f4f7d1bf73740aa1806ab113df1c82901d59db26690a388a3d17be3bc88c880cb92501e2dfdd76d1681c7e30a34e4469f06b67670cf626da64b3bdc160c82d4f4427217e1bb3baac2f6004ffe4f8da116e1336eb2c217957defd6c15af86195715fc5ac9132d339e754d00b308fd090316c37439e743277ab213484e5a68f4fcb2c65be736e26fda553648ba748f1a17b21001bfd5f02d54d395b50dc4ed81302c5cedd11bd5d9b75ba57cfe4a358a0ad3ca5e1952d402c72a7647322c931a2d913bb537d331ac1858694a7611d07e5154783d6d1c18efe984d82294203c3278d59f5d6a524e295ecc6120714350b9989be5c49fce14982491cd3ad781bedbd82e</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-surge">      <input class="hbe hbe-input-field hbe-input-field-surge" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-surge" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-surge" data-content="请 输 入 阅 读 密 码.">请 输 入 阅 读 密 码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-surge" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M1200,9c0,0-305.005,0-401.001,0C733,9,675.327,4.969,598,4.969C514.994,4.969,449.336,9,400.333,9C299.666,9,0,9,0,9v43c0,0,299.666,0,400.333,0c49.002,0,114.66,3.484,197.667,3.484c77.327,0,135-3.484,200.999-3.484C894.995,52,1200,52,1200,52V9z"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">本文主要记录近期的近期开发计划。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="加密博客" scheme="https://www.techgrow.cn/tags/%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>近期学习计划</title>
    <link href="https://www.techgrow.cn/posts/860c6c02.html"/>
    <id>https://www.techgrow.cn/posts/860c6c02.html</id>
    <published>2026-01-02T14:38:21.000Z</published>
    <updated>2026-01-02T14:38:21.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误, 请重新输入." data-whm="文章校验失败, 但不影响阅读解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="c8f00ac8144051377ba307fe38694385a15024411505406615450724ca4bd16a">4cfab7e4fdfd9afdbf1592f9652b8a199f3cb3b67d053a3a2b7db0e886465ca1ea14179f28e40c4d184809b24d2f909084075e7e2b3392f33fc9e77db5e7d5c8c75c9f5edb502c44c97d2936037834eaab784e9ab6bd875cbc56679f666329099d4ab89202683abbb75c823d1c8424df846096a5e220e8d947e3237c2079fccffd2ebb407025d74a55454dc8b9b915621dc936a1785358ea4563fc1d860f7b27f4f2efc4497cc955ee3c3c79ef2468b8f5f705ef1c1b0146ff27d98aadb9f9b269a7fea8268b36b7a8eafdcec14f94cf3bdaa5d35f236a25c23f0a8ec54a67b6768c47cc3a69f53ca3630b03cdda249c1862a61de00df4546ed43a441e16c36e2c05a9e0f8cd4e144c55b719c7a697bac7e90957c47087d36dc98afc80b75e748562c6d0e6b4e47ca91575f1758619420cd697b1e042ace49b1e60bd9e1269bf493a3cd90be137138bfa5ff2ba9dbf9c7e88905743edd9faf92b2d8a5726b56afeb5a5eb15f7734a4c4af977045589863cf1dea44b3aae59cc9c1fd517822a14c8e47e7cadd5aa24669b2d02d01078fcbeda47f35d2da2a353ca716ac21de1f4691e5136d7463bec82cb5b58a72e8dfe86153d1893c17369835af18bd66501e6d4559dee343028c34f87525b36beddfd38053a4a2833546621a8845c1132d4808ad89133bed187f171916da2091b4973511701c16b37d026e1c67f27cb0e8046579ccdee9b44f014f413d952254ffc173125f214dde79cbb04f0556811bf311086ed694d656a234bcb8f983ac41836ba4aa20363eba544121d736809b5fe9d80a1c17c99b0d1651b885321a4ae62a288af89150c66fe9dd6f9ddccd7a5fd78ae15f994e65743948fe8b46a5230d20258a412d3ecc33800abeffa33f4b3408f197e06adb2eee6c10b227ff486e3f94818c8069957f8d50128216cede6220ae004687cfe759c7ae4a7e52c5f3a372374a34c7141e248b768167747c31ca73251a4077ab7f7da1bdaa81b30dbfa534b700543a34f32454debaacd801d2359437bcb8269dba80e630ed619e5ea3c57b6b1c896bf5fce44a78b6e42bde3bbde6932dc73af25db63da5ad65ad6eafe1b6f00331f135da50e8e0fc2987275e05d41a86b007cf771746971c5cf4308b9e743949a89b54449b96a7db81e014363c4b29167d86a31373163e92919c114e6d3d0ff60c248f78dc7aa400545d28aad057f381f1a104c9d4becf6c42e8074baddc87c0711efd811bedc215fef0ffe19e9f767d582cae3089d68a0f539301341b62b70ae509b56f7bc46f6d6b7de2d4131b51ac5a280bc7807db539bb8ba4a3f33d9935df2ed6c44a8844eb9d33cb882c97ee5a84315691c86b8dfc111bb1902f9b89b1c3f369af52feece8baa7d4b7ed8cc8e664fa33e7bd646dd1ee1f1fce68c4d9771e9917953e4efeb5278dd11328d6db2f2a58682c6ac8cca3a8e13f9d1bd907a5916d03ca29d243a4f75855c7d5fc8e05dd97b2021e4c0ef4d26646edb0d493588bc22e915b3cadad638336b1f947f4f4d77f2a63858570d82276af131c7a81c1b11ce162dd214da3d73f4a75f2d76a730fa360f3659e2092caa6e1207a1e134d77b28e3b5ad3c09f48de0ed3ff31d28348b42e74b81a76fec467e5d8e792c660407f5c89a16ba5fe34bbb1e22d15aa15df6d7cdeb7fe6bb888d636da0dfba1457d687917f45cf271b863e8009046c7d6f1a6e91478d9bf08bf4036631086a19b52d750828ba3ddc049dc1d90507624f7e1f27d247617fa052bd2c6ed14cdfd025c431f8a384733142d57f2175de452b541b7164f909ee751212434d8c807b2cb23b59bae401422103c8212e9b6b7e4a13862e084306ec88c8f9e361ac1166d73d7fcf545cf98982e47fb3dfc6d04a47b3eaaaf3d48146a93609583380a0fef09b4043f73e8c6b6ffd2717785b7b2d7bfe497019bdc80d1c2efad129c6c00ccc80e9e915effacddd182f033324e01bf08631d07aadb692f5eb1cf99b13e0bbd3fa9fe170ab3a1a07f7c2cc7a22300c6dfb7ce27499a9e0dadb21351ab7fbd802b11b113d7b3618c4bbf2a484fed5c4c341ea07716fcbd2246535ab9077f28b018081d2452f79b8f2930f50d2e85f572a81546099e30575e1a357f22596e8db39f8e3a802f8cc32b4d1e81130d984e5b6f74f4d8e47a98b522dcb4d6e9a1713177e1b10c8d794649564e0829ad73cf2c890b4aff2d3f4a95f3f57ed16257261ed14b8fe8698d32a3fda73df5a20c2ba3695eac5eeae2b1497d13622b05353d5a63e20668f3632253524c81f6e9e5b376529630fe169774ed4f6098383010b01688d0123636c4bc4df0892d0c9978bf892c5d07950d1080c405e8f9edf6facc0cb0720bb0b4ea69b7490fae6d809c2c4b647724ca5cca621890d654e0054d2d9482ff380cb9ce02564d9c4cb83367bedb156a34c65ae7dba1b3a7030c98d51098b4a90b93b941d9f6d941a7fc9a56e96c8281f70ffc59230bab686b0ff94dd052d96672f83e6e1f20240394a89de78a563e055d3c1e2230789270edbc6e4871b30e038eb6e2d70c9756739f9abeafb4cf4763dd401afaab6e8b7d28eaa946938ef2224fba7ebee3c0955e0bfc177b64826dbda44291765079976b352dd800f2ec099d9d96a41ba984363b8ccc03cd7c1e67618c1f916759cadb1795b396e81a03591dd0cb88d06bbd935bf96819a0b3f899677ee20545543b2b62705b187a710e3f1e558403ee7e8885c3d60a31d53ccef36f7b80ef2d98beb0cf864f40f64b6b35af29e8842b350cc55520ec31b0f98382ff7d5e1396b0f835096e6da4f44e293f860d9a61fcd874c3515d0e9c970d9c2849f8feea51b6c5ad300165bc2904f84961d01d0d56fc2d9c725c43b17f94d9f1bb882b9a9bb4d5fd94c25423e9490f36a607bae8c1545f85e3fed4f4f437be53dc368d55cb4785cfc504c5ee0c78b4aec8fb67d84c9dcddaaa6f706eba6fc22377879827930942c2e5d49c6c35a2c1036e081dbf4bd6df0a2d4dfe492a434e85278f945ff84cf170519d33a5d4913823a7610a1a06d4c08e6fb56e634f6dff45835193185de8c44d78e4e9941c93ced089324fd70bd38b13864f83482c0e01ea36f55f90f1b2f752cac8178ca5439505630710a3bdda6b37c5e2227c73e53da2bb1b827f018bdfd6ce135a6e71b377580536e84e861caff81872d9541f3d864263c4c2d94aab9246a7b15a762116966ffb7f656ec1bc0af79d17a8c5459ded5bb7be392abba048e005748614da06452ad7c17ab4a0d7a71fa7f92b45ad1c06d637bbf674c4c7134fae11086c52b88429f728f20df23c76402e1ec3ef8ffd4edb6da08d19aa9af550e2600f633766a146e0ab5e1aa7ae5c5d6f2d22ba68f8e156b69218faaa613305b79fe226afb2b76c1d8f9ffc513379d588d11ca2183fdd232e69514029dd16e81fd94d852c333ac4c4491f83eb39731014679fae09d06a990c9fef72e9d3a2ca4bec17e2d9c186c3f41e8b8efbdbbb96f3b3f515bd70599c10b2dc7ed621342cb852f2d8c63d614d68d963dc3ae9afb0f233c7db2557f2c231c6f94fd7f482ecdda94a58d965b76e10c16b7b00efbc92768b5fe59fdb8a7f763e7947352d955ee12b71bbccf5d01728e73dca3baca3d5093d8aafb686bcce50933f5398f2342d010645014ed95de5371fb611e472746a9fa5346aaf497b2f2768dc628e433edd2769db9ea95882b6da8448d5cd87c2efed530d92511309352986d39a289c8d8db256516f6c37731d432c6702dc7dc577ad7d9386509f3fc885fe3d6327ac962b59d204376db69924cef74a7a9973d4f752667066ed47d53f35fa65c4bcbd025452a4adc8349679ef411646fed07c4dca3ea3399c61fe0f16c3deb7c13b8ef6bad453b4fb495bb3d79f59d70497773eecdb6eefe158f4634b7dc9e0e1ddc07aeae3f7d49ad0e96483c9319a8062eb7a4299b0835cd3fdd5875aa10ab484ff3a5a86c0e25f1e434e1fcd1f0a421aefba4c2ac26a5377c8fb069492b72533638cfa473c5bfd2111a6c277e07248d77e647f23b7c250853c6ea7c1af148a756184cf07acd171cb2b97778a0f8a8dbcf6c0442775a238ad39782eddd9079b224bd533411f3308fce227a2f837a598feea0cbd90867cc94b6245ffc5b21e2029c18bfe2561e0352706c717750e649e14da517742a5a3656b0bff2a7df3453042580b703f6c6cdf45743ef0e34eecc7a4204898eb6cb8c1a9a71aa60ba433f0438d4a977198186f69d854ee762c0d498d1cc6000ec3b0e441eafc89ec9f70af36dc73bb6d9030cbd97f915bcd11e268bc04ab69068ee9039a79ea2f114eebb195d04c77e729f24274db59c6ad3cba01d95aa14330a1e4f4f88ba13aa6b5463a6164fc4cffa1d526286cfba1b1b421f56ce42ca90fff2f2cfb70dfa11ffde21712103d74e19a2809b0f8b75da921d19ea91e6f71152f7b04d406bbbcecea68c51c1d46b045f78f84b0f464d41341bab2d456ed189585e4d5816572b7105f55bef91a2fe1b29bc8966703007b5a542b785146dae149d2ade3340989919359a5e89a496a42a9451cc82a343a9d6e8b545c6efb68618a7515fc7484b07b69825457883fbac5b59f962cb010643260d1043432e7dd71094a26a8f59d79cbec99a99ba805d81fe3386edf76acfc45fea7ea66dff9808d58c92b7cadb9b577f33472faa31457cb085d0245587a11c59d562678f30be18b2863d2f49a62274d520f57a3abf17f8dc60053f8e29671cd0b650f360b08d670d9f44c563f118412a2fd7cecf7fdf87e5a4e75ae4ab54ce62a251305148d73b2417a7b5431523c0169e9b4662d7e84aad66a904e5cec46ff61b1f70a31f87dd0f2a3e613cd4a0b167275bff2bc1117e7ee17e2e87070b9cbb9fd4255f6219513597566d15065d8bebf9f59d7d1ffb0e513f57c703bbe80c54b66a4859343cdb9b411fc2e67375b893cde020168a4ba4fca30c2cbba96bda18a27f91df60c25336a1ecd707668d1dd840b63ff62aff5f6be2d817bd62f155a07ef084d52c571ada2bd6f5114855dacf11b26de64b09f3121919bf017a3f045d09f52c5c05bd5098647f9efeb2b13bdd6cff7457460d9e8fc832de10c3c0b9dc8e9f039dde61c300567200fcd797a660e08518c64c08ff97e2339969b02d8d015053d8b087c5aaa06e4f60478d20418106802e29903205d07527daca79010151e3ea989748f9d00301904edeb40f816b0c9d8cce77c5e3d14c0df244697ed942f68f4c1fb52d9610419b5c980ad72caaa4cb6a0a0523ff61b619a2594267719ec686c67261cfad3d56f2abbdd50e97e1f6fd028e066eab86b1ce99ad3b28fdb05284e6fae46ce8f145391b0f6aeba63cd60e1b53df6ccd8cf2a9428d7845f45bc44ec9d1e6f20838775cdff18d4d4369919390dfb20f9c0de9c7a2cfd30992f7173943c43c6a16e41a93820481584362574165e8589b3e38425585eb124d186e9ae401ab9c1740568d362abf892696064c76e3f91faf49dfae8bb8a8260e0c33b18da0bd4e78fe7fdfc3f167e4968a94d878bcb9904758c8b6f2ecdcbe122ca21394bc5f2dbd185d3dcc3f16b2b7397ad4dfc40594dbe4040dd14f83ddd9f48dee8bb20a123b07ceeff8d93f24b5ee6a5cd0a3762d2f324a7a459a9a044f95d25e8b3027973082da46cd8cbce165e1eabef80464ece16e9b846ed51430d643d8c8cd34db43e4e925f2a4c560d5290316dcf8e42ff8325ebecc87e4e8bb8b553a4a81b16c7e31efa5514d3493c8919c633055699e567d6e36c032d278742f2af37fe198796a9b72d3da605e7d95773935aa8fb984f66026cf82356619d7bda944560684bcb29b82214cc75f0d2ed2e072ad212004e20ba8b9b92632edcfd9d54bc19f65c805fa05aeac1c77bd00281a30e86898d7ba53d58be2c4d353245f5c3bd096f330cb7cfef320fbe271c587b8401f0af8b28aa9a08892d265d814ed27f196b114da6efc60be8cfcadff4f960bc109421245382d46eb4c082eed695478dd93f4a32c72f912950ea48785787872bbc51c8dc2dfd592300183637260d94683e4340c2831bb8ca202dd8718283551258985bbfbf0edc4500eab33d833cbbe937cdcab566805b1906e1162a47b9258f4bd17bc3c62098d4ffdad7d0c0e2fb069a850ee08af5376cf2ac6319ee141049d650ea3ba0bb4e4bfc5adba802130556919a3c1d94112307750fbbb8776166f625aa8731fd9f56dfbb9d611e06106b944c0052db30496ad5526521688be65bfb889abb861cb0769b1eaab2a50e6073b2504ec5e9e497249c3027d2861f71cd2c13e47dd5bf1cab33a3f4a5a4e708c6fdbb7e9fb48fb96ddcebe084375edfbc45c5b8f1bb1dfd965e0a099d347c5aeaf76f8a47c86df1be7f3d7e2ab276b68912cb45067a423fffb720cfbe1908c6d2b0f4cb8c68c093eb8cedc3b852d535e90b63cd3b26e12de71fcaf30dfc0e7ddb9bdb4dcd2defaaf5a66150e2e6e6555289238beb8e899f8a319e239f051d470fe2cc881d5aa763c9d4acc235d3a1fb5463cf21a190aaee42c78fd4290c70aa7c86b57513d6b80d3dd66cbb8312a17632037e482bc90983c691639674a76935ca8db1023bd29018490281be92aeb3f69a7ac4d0b0c3733b27022a41b4022949e398d6f3904454bed1c6febe19278665b5ab25aa1bf3782b30c1a9b7c3027b490dbf0b661c9d8c6be9f8c5904ce44de817653ab99cc7eec09c941eece83bf9bc21a0cfc6fa929ac7979f3840c6805d30fc3cbb8f49bc4977992dc044aec8640354b2a3e2aa9899aa338f4dd9670f1aac84988a2e55df90c40cc3ddefb8e78da73b94d57342851e232e1bc38b912ed4dac037fd9fa2317ffe5bfd8344e3877e93d3b0fc86c3e69e87facd4f787fd508d656f414dd10d2ac2e803f95f0373abcd84793b379a2e530f3e851828dbc7ba5d77f847c1fa493999137e81ffc9f03311773b8c9d8bdded185f0b98d727fe339f68a80becdd48738100c41f53da3ff5f35f346d33902409896b288eb2c148885de93ff037017f068f7683dae067d882ca1935a6ea77486a848dbebae30000d765511d5bd4ac5ef0ec0438ce09bc8a48ab1eb5e3f9e9c4e504f8c99d1720e38fd5a62a93ab75c76ce760eef31c2adc7bc0025c6766847f51275ccd6d315f8377c93c69b0b1c31b0eee40da41b0ae782a61ca91eb9a305c6ee5117195cb99e7f3648ae49bd584f8156e798681f559aa28f58348724839d56d8b5167ef9424ba08555687e04fad27fa6abc29a8b77f7e0143e9634bbd7fa22a1213614e71e9323e79aa6c59538b866a05f07898d05eaba0f41de2812b9a2d71a5d0f36d6bc0c72d73b8c81cbc6b108dbc3c777bb801a7a98c20cfa930a39f90cce56830056790860e04c9f79418411acabbcaf8c630146841bc322b913b591cd73e380b3d7a3fe1c9d7e3c5b76356a56541ffae0982b0bdf4ccd06eae3af86fb30a4382acc456ec202c2f6546c0c818cde7b519691e4e74469864b7b2894ff7c46ab9850c9a52f690a84a7231ae191310cb41052704a519ff9358846d4f3387042fcbeef9b4b6253eb987b718b6d84b1c871e9dd9d317c4275a6d79619aa6d4626910dc75b49e34b2e2499755544476d79cc5129fbe6015676ff2c7ca0a924079987baa140055361a807541f26a5163231e80f687d55f90f9308831650f6ea646bfa45b93a4b454f69976930300f7d7a675195bfcefa3da9b4163dfb58bc00ee5162924547215e6c9b60a0b1d7d457ddf0d2a31706fdd4c4952d8df34242477045dac7d13d24cb0f0475de1afe6b4f709d68996e1694ed6a57b99bd8097a04eb7d4050bbec524486b040e1d7b6f3c7f57c2b79c2ae6c89f31a75dbc7d028cd10c3a60b19edd5e9a6afd6d34799001a1d477573467c7acf9589c09c0769e3f63373e3d4f6cfff7a46b25709b4effd5f0f6da2de9b8d8926c99fc5548ca035cccefac170eb542f7c42a6e0f99fc0a139b16be64e6485c2632ad17d5300952309fec2c43c8e098be2ae3ad544e8531e8b24c98afc57b19f1aa2b3af647e4bd35a5d88f8a42595c5a640d116ea14d8ea60dcc986775085adccc01b2ad520e516342e0a22b24d45fd367691d87bf8d8166193f5d16424a18829526c184a27a169e5b1b347da391b47d055a5f40a9d9d012f718278cbf60c59173fa60c9ba868d15aabdbdb15a26f71ab52df240a920943235db5209346c55916884722c3069ab2e085790de378bcb970246e0ab8f5058fdcd792dbd4ed25dccfcb84f345ff3000e24cc5369ea2c74a2b46e029e3ba172e27892f8cd858d9953347f8481ab8e6d714343e26b126aa3182aa7436c8d02fd4026cb19915731a4015ae85607e50173981aee3a86dddffbfea02521f35f8948dfe3569420ed17469f8288ac1cf824ff208248d18df6729c098061a5ac9a5b8970b879cf533abdfa57e0bf36e1aacfee7a18959a1c09477dc28b120bc2544698031e63818c038628858dbf66c35ef1146f03f6f146fbf3994347eb070f2dd481ae889c7b5a17335ff5760b56dfc5a46fef3dcd3cfe72cd677f93548a4e7f454b120de3c01e8dae8379b0901f30c3561cdf122a9ddb2ab7f53b0ec4d8de5b75b05c7ae34d1f013eb64afb9a1339143abbb8d449b862c42e1c040f99a370927a2ad564765d790cd4b271e3e7ed63b87b8438d7de8896e7d5cab351953311fa82e5b5651784a3eabbd2b9acc0704aed12796715cfa0b8b48a0f64cf3b78292ba025d0b9306fb0cd7856d4d0907c1b89aecb976540c4ae849190ba61c0a66f196c616348a37a30f5e2e2c7ea6623401daab7701f5d17ccf854eae1c1947041bea46e06312d75061c3382e84ded73348e8683b23f8ea5f69e1e8e767775b077e0f6b477c05f766fe85938dbe96d50ce0a543f59d1ecea4f82d81a4c6143dc5634c3558f184c225ff1ef0b6f5642438515a1f83eb65cc33a6aa16e3ef67584f2e23970575a09e8b97eaa92bf0f2567841839a86738ec5df90e5985678e768ffc2cf68407210a580ae5eb69d925af254fa7818b140083f21a127c8dd01b43c85ce11c4582001cd1b6b6a648e5d416d593ac4599cf57a9ae85fb409054715572e4d65c01d40b199642ed7e57cf99e1f0c67a33a7b681b87349b158b1ebb63fd54a71e35f02f2124d96edbe72279410037e5af67717a522f9ad0ee6f9612c017463b494e3f2ce00b63ba960c3b9e339fd512a3f1de4e4ae678fdb42092178a1e88a09b0ed8d3958922a1f8ab0a7e69339d0e9d0aee0858a9b796661a5ca3a3766857209f41648428041bb01e3c81051cb4bf20edf787ad658e137a143250972a377081585adcb7f4860ec28f05a1973f6f2878d53b4b60d7c70863f937f2a9044d9b717c9dd935e1672fd2c22972cc49f365c988b5f3df8c7a1acfe09dc79b7cb7180dfc45894414dc2e6850d597abaef8e3dfc541d6ee0248950501014024f840d37008b0efddcbd72d53013e654d6accf49a4b02551ce8ab52e65f44b665a6a3e621a3f1e4e1e212dbf9ed134c27769c35e3888c7249556ab425f164afd78937fad13ff93f20ae82a5d2b8da9174a610eddf0683d84ec3a816016f7781a5c50298eac9f8018ba06ef6cd9fa87e046405dc235e02424d2bd2a3e80672aeff775126c24c64afeac789297a62ae0bfe3cfbe78747d8d4f8e1f6ca21bdb4430d059cf11d3d94437a3969e03643c074846f9e1426d7f4ab4a4761d638838801b9cca8e0e9580a066cabdcdf1d9f99bfa74a893bd9fc9500a5da15174a6cd661bf926bc1d87b3cde4744c64457aed0685f86a0b9512f4cd1cf9504d97297b4492a87817979535bcce1a59126ce5943eb52dca082794d110844b6a8ce0c0c0169edcfc641a597b8d3506b8eb440ff6a3e1d2450b119a6db87c856304bb03bbd4cbc0174395429c95271d9fdd70448cfa607a982566417b7feec128e76948cd0543f4843cfff09e47c5281d193409180f465d19d92fda3e77918639ea166673d917680e9d15cdc6dae5f6ef3c824cc3ec631ac509369edba38497b075f6aa6da83b79d83c29aba7cbae1429f75385b15d035570bad7ba305c8be662a18236b2de5f26bff643dfa5fc5644f81192dcc0b8d2b4a85116de94a96cff9623906cd2b754f43264b46f2c5e76895478876c3586cd607fb09b0409cfbfb14ce25b0017eb516fa4b23f7929d41dfc2040095e29076f36d694a528346452911d10c3dbe2d8532ae8fe9ab98f62f9590eec28b96356cc6ab1ae9ba765188ba699ed4cfcc1a21980ec8c37c61ea7c961fb6d58fe7fa70cc14f23180ab8590e8c94fec43b128043064d407cd95481370622385291cc54485230eba4d22185eb548484a82c6d6496551936fd8446b2a3c23d04364fd36b743f5f43fc2a5a658ed327af36e5fa94ac69082cd572695dc5b02d00b12b6ce8c3e6e12a4bd5a005dbf68bc410fa9ecf84d204af54ae17fc5ce8d0db0cab3675706366d1d3b1c182901f07fb19dbd715322abe95a82a08b2111da057bf3a434e14123cbe6325be562861d1ead9d8e9b0bf140b0effe18f044218d31dccd72f9ef8f8d8587a60955885635942854b04e4106e69c6d04f57e7f7a4f8e86ff435c1f76e506e845decf2a488c4875f24d1c8781a9904533ac2c7892fa68ad32dce7bf8a93950b923a0de00594b1cb1c3c4cb01b1b1976cc15538c6fa60f05350738f4165068d7c13ef4404d6ff15fcc195b2e29fd2a9801345d0da4d037ace24bd639c90f8648334ac5364b30dbe58e341d6c695866c8c1ae57f9f4bb134cc330f5f717f2927a65c73a9ce764bd6a91dfea104805da8b5d65d44b44f728028889054eeaa035e81fe4b6ed92003aa08a61d57e783b360ff8885a1840beb19a7f984051fefd10c75ebc0da910267d1f7e197cfa93e1ee7587dbbba860b6fc9dce5f15e93a4ddebfc96ba785cc61f3e5fabc972654ffebd078b453d87aba5d6e2908d9ea107726a70cd04c6b59f601899b11c8714aeea998065dee17b0d194ed5dbf4c430258cb879e743f7bc8dddfc3ccd572281990ed68b9d225e3a12d92638c2e79efd909d4a2211d6897c36780e94266188de5eb243574ea0ebc7ffcc2621788aef7c30ea125f3dbc4d0d597ca1cac266b8e6d463f579fd7dbda5bbe6471f121ed08444620d479c0260a748c9811c61b48e74a404c3879be8e3a6fb99e1f253428f0d0eb67a0abb247dcfcdd906b4ef64f637851990be7c407e142ac3858c1d0fc874982f5d810deae418854df9ff16c1fa29023491acdc334f3fa775b48f86c1a7f2252acd4924e39b7db5fb97cdd829e0371e12d4b93c7d3a01a35dacf4b1a47a94f988ee42cb0d2e57e655d04aab5a7f8059668012caa4734e99124a7d0a6e24c7b2edea4766d4820c3d7413f2a4c7452af1bb663a11815819002e9ad6de9ed21bc02a147e8910bd91c03410ebb2206f8c42fdcd8f04286b58698d54183747d0bbd668c50f1865faa54f287ce24468e03e6414359af25b8ae3d7a9c518b641859511cf0748c454f7eefa0af222899948c4f16cc14ae930f08b40f19e9176c1745600b30a317bb6314705f60584b0e0258e1292d9291d4499dd92593bfa7802cb81415bbd67a4c33e73415018d3f7c19a64b0cf19e0ed0ccea4865e89bb1041af9b039d4edd5d67fdc489914ac619f7f91a8913d310c58287f16cc3ae20198b975085408f39448599cc9cea496fcc4a18f8b8d67cfc5669df03f82fc64f901223b92bc6b3ea687efe23b2e851ad7f454ee0ecfc419b4fd08a86c714369ca092b39ae1c81545f9cb583348a142201e7d8d223ab1fcefe8f8e2dc16ca998fcfa71e935a139a80b4ef6199f421b7f0d38b77696ae04bcd527b49cb4c11f7e344a9b9e4aa5b5f6581d8acbc38938d7b4f0846e789de18759aabe83c663a01956099c47dde52b2f876164e7f8284abcec8e64581add3861a329dab768255198b2400394a40df6f63c07d1db1fb2c1b0ae69f3f687219f73aef361386a5e583e8525052d8ef58485576006eb38267e43f13c339e37d49a000a851bbe5489407a01b0a8539e333d7cf48bab344e3c544922ef234c3ecc6744c62b3095276d907f96d6c0c3720b8a877b5f73581fd5f3906d5fbcebf00f3295160c5834ed48b602caa497883141a77bc358afafa2dec6e5c25d92ec89fb52e7433b976c624ca805871b6ad482d4fb62c0ba89a3607de08f7051d1397bb21b1421f4ea2a1c2c0f44419f0b365b43c29f3393ffa7e9967d9c4ae973766ba62aa3fc0e5500db4aea871fe8ad0ab8d801ae027fcf2071d751a84cfbc9a4c7bcb206cb71df3474648fbd3126995f9bc70fd8889f239ada3cadb92d4e16bb4a801908211f60d28700b49e3de012f63f4f85badb7770af4dc126575d395ba13b699953c38d9c33aa6237d23df42cbb2490cfc19e381a68cc1d63f22ffb092c2b7bcea2c897f94f270fa2779de75e0d9e169a8cead160c4b37e76cbfc5e1ae55ea0ae8f19d7630f07569c301e1e33d24a244d8729a0842aeb67eae9bd0521e7b970f38923edc9c215e8e17f3a948a4e94a27758bf175a993387a0d961e9e664cf0711de03b4e986d8b9f72f203163ab42a38e5dc572b17b6366da9ce824c8137bd3dea9644d5c97d02ed9624754e34c267c1c1801fb988ff1b24ad57a085f267d04c024bc38c1fcb144697e6001048ffbfee4cf21c11af71daaf859a230712a39f6aeba56e072d6a68ada249cdcc9ed47925c39095fb7b945e7b81482ef664be37e1710f534c588307a5a643bcc2537cc64fdfa83029509d94ad75c9075497782d7d2bf8f22ae8950c6b90c631b35ae617d04af0e7d03d5d6ec03b4b9564566d1519378d5d9e982169f156459098f9bc7bdb6383bfb92e94f0a2d159085114763429e38fcf203943b881b0dc22280f535107da0464e668f855938592d6c92caf83ebfc81ccd58729689c399ac3a9988c41eea25ecb81ed95e26998abe99cd23a02b8c60c11adc8cebb9dec6c6b208dcd36dc8943c0172538123fa9486b3f264d4c7900f10ea9cb3e75a92b165ddbdbf558615b31ae20d94c405177759bbcac043b687ab68cebcefdeb5f9e656ed109d5ae7dc6fc72fa7ecd4d328177b17b77d7152bf0cc2a3aba78420728d8c8e85462471c4757fec32a1fd5bcd27922ef3a6c43177633169a5866a09173fb592c9301b772eda2b65657bf966b9e56374bbae071d37f7427ac9743a9d24f23a63d872c12f1c6b0b5514e7fe7b54928b41b78dd2636173221cd931f9fb26978c664528cb1e5a68e895961c78312cbd394d12965789e471ad01d1da3ad21c0bbef2902b9ba96086f3cf70652b9993db25ef15b20fff90b2dd87665bcced620d501951d5a0ac4e27bb26259784bd6afc2b40102b64ede24eabea82d2adf931d500fcb581eccde05a7c0ecf29cf5e5c1f052d25c1a10a6d8059bd4894a19f51d2da05632a43b45883fbc6846e85c5ac6a983ba88c9b7bc96142f642ce70cec5bbec388adb3aa0df85bc611346c4c3bbd908132bbe8b9709dc3b25aa6ff9b6a28c46e7d00bac7e6d5751c2d2e819da1bb302df576ca32c2433a7443196c1ab81d8cf091051e448ca31d181ffe6b1f0b4d8d087109edcb68d96835a35beaba4f017963a7728666ae1fa676b85235ac65c113cfdb6c8097d5823452d6b786b992027ece31553186a17a1a89303e22d8b6ac1c995394cff2cf5a23662cf6036da1d5043189f66efdc9a3569e91f4f806c52aad17e037123a622a9bc5e3753bea0b12900d053f950fcb6ed2d0c6bdfb515d289e77b6b3284077f3b1fc8cdf1ef5ffe423c468612f8bef45af8782ad84c9d8389357e6dc78381c97f185a035d9f6fa6ee7ed832ea16efb0c8c0278ce61416980c9f867171528240208c671e22e664716d0224cb738897c7d91ca929d5165d4879ce5025a67b87502b1e7783dfa9392612c757d7fcb8027eee9fd74d0c24d37f4d509a58c0e15731a433c306ca7786d6fe613ee9ab1fa73382f616c6d2530225d6530373c050a5e34ce060dae83100ee1de76a5a10c6c8b9cbd241e7b1ed02cc0ff820b4be6be1bebce82b5fc429d977aa3d297fc5f69dcd62caf6dfe612fbf7dd1d2afec3f519552df9a6f475ff3a21fd7d49f20ce6f826d476fa56d922132aedc99ff0ea3bc75999019dccc74c6befdad121ef5b80c3609498aad2dded07d2c755cffde617403e1f418404126e1fb29a875a0cffff899bad3af0c1790de2a9bea8fb041462c6698840ec0a351a47f1ef09d7282d30f11dfa45155d439179619fc6914c03f8ec04553792b6d219de622912147504e7b45b957c5868838129a3af2301f27b95e812993ddcba68141677d652b1f21b179a51cbd66d98596d52e65a9788d8baf08d1e26503a1e85d9f4752d719b0d543556a1a1c8b431cc7cbd5983e5aa9b728dc685a45be754bd1fa2aa20225ef8f773264386bfce4f601807b94976055c5a0248f30ddfae4f903858bf06b797abf5e849b34e6d49a2486bc646759cb6b7786dde89588a95153ea7b90efceb0597740e72a8ce589645786d903259ef3a61ffa0eb363c330ef2b0384a906c84cb732854b6605775b10a2725c2f342c263dc9af26cd07cef3c8f09de64480a984f6faf780b8fbad17f8e2d9163a9e3cca6c0b41138bc0eca36e8ff6d0f20dc295ee065977cc2aedef603f64f03fef72ad37c7a4dc645b08d64f9b25f812333ce4252fd2ade441f6c7d0ccd0acc18909085b14455bbcc1a5ed9065fc5d5b1096b71dc03f73d1d7d7c6d4c13a5f6a9b145ce25d1574d597439d8b791b6da4d1bebf3949c8de1e0b864cca80b4e49388d57fd5279c09e496b9a9a34148b72fe18653ebec72cfce20f8a1c96e060f23165335b1ef8e1e952eb0b092ad2e54d0a094e40e7b805cfe9e039b3d1c79a1eccd243b05711cf435cc675b4decbea012744b5fa05b10f43491ad2fc929ec5f9edc688784fa72d152625b29cada2d8f5fffa94af4ff492787d4b5d7386de8dfabdeb02d788e1224b5111e5452d5ba1475d2e83e7e6a0e7e501db0cfe417a368bd0098f3a7dc2eb80bdfece6c0b35ccd8a96c17d4da33ca9b3cbc9dd7bda9fef32fce5af74186c863e0de11bfd8da6cd230aaf497528ea380addc41c82e56156f55ecd728a46b544e15d1cdf58059886805c563292939697cbafccdcf944e81f9e29f38781e5c09929291dc150e3b125b2c537bfbb5362c94a0b0a673382cc8406d9e3be373b4a2e945f0dd824a99da75b3e271a6af34eccbe0c24ef6354b602402b147493cadcc802b532bbecd0258d7e421920faf71113557a43fd19ef7a1ee2201702420610cc7c89b0a9b1784150986b31483c03f46613a57eb2e313c32272ae28dcfa360198e237f4329b9515580c67a4c277014cf79fed64842e4ab9b2685baecc8b68f4f7034bb0e6500e58f244c923386600ec158f21855288a02714561ef8c10a97d92f4ce2b69a5c74b0d8beed74a93aa2878b23f5fa5c271d3ec8137f06d92981974b486e2cb103cca3042de53c5eab4b8adef7669c0aeaefcd1eda848111a411ff79b9a26ecfff0e001a296cb108da8be3cade81456c22b27c5e35545ea13ee02358a29a0eac56b14c977793bbc1b0c245b4e99a1593024b555c92ce9263d9a847ab8b29d65ab45b20830ebc8ace237502cff34572361d0117d2940c282e4ceff6d9f17a86fc633d553e47fca0fd18efd79923bcc05dd84ba13a254da1e507867250d6834951e1a50ba30a76b52b7f3aee234d2ecfa07147d7e43598806bb7f771feba0d478b6e916da6f5b16d5788141c3f43b99b53b3f6b96e7fdb9a043cb78b3d447f79231206e323c58f616776b4ca096da1cab696afbc3c0e34e31929ab01e6ed7275ecf2de44f3ba13423f86d16b71241d010548078649260e75fce7cf45a48d08c54f6f8d836b79967aa8405cfed6a78f16d76e2cfc0de3dc608e9b03db3aeb088864c46834c9bcf351c09aaeabbffe3b5a40501b31ad570a637ca2cad1e027673304a6b25f1f3157bf3fb375980303acda0532cbfe4aae7331e6debb3a2c8eff75ffbfd189aae49fe5c99c6152ad3edb421f5263695665221ce4d155a2861d6ccd999c3e1e00b803ae1a036f7bcb9394e2262434f86445fb4e4f07ab73a0bcd857c5afc1b034ff0b1e51d4d679206052f8c0acceb3c043e3f677a7c5acf6a6690a9523bb009373926d1e234ca4126aa425f26a1bc225543992c7a328421016b3d671cdc627fa506bcaadddb08a7deba6a2f33129fc3f67b0fc44a9a9e4b690bd0244b436e60b0ea3e89b0c41771d441c671812a441a1e770ffbe1e94585bd40f18c3459e4cb6f098fcb168c850426fb9943d1b76684f9383e53a12075b4d6072b18e76148ad6b67871b80d2f96de7a63851c801a193e316724ba06b96c1903d614c71ff0cc32e72443dec9dcc674837cf286c56b5799a83f406cabb969a33a4652f546c0b9ee41736bdecc63ccd78a1d566acb50cd10d0ca6cc4a203a6c9841d2c74576fdad0c2cff519d277ef17f7cd7b4246f3f37b36200f4959925facd3ae75ce9ebc3f3bda24c6febc53aaf15228d4ee32955297d48af0386d7b66916a4b78838d2224468d06d0057d544ed4238ba368e1bb9f22f1ac9b1a0d8d3787e553373cdefc51356615070812694e84eda750312979bdcca940f1e35edf483659c47443f0eaceb864802b0117458dfc791b044806f69088fc457e250b6071ad53253ddf60e1161f63452b52111cd11c81628f98115a97116d6c8a96d19d5812dc1d1b87d02716207c9fe4c144d09b29bd5bdff5102357121182b8703b1d3d86b397f7f546471d271d08d0af0888f20bc80fb6e80f95f2fa12a6324b5df7573afa31b50c5ffe1267d89ba390a2ece50e0d3b783ef7b208266f2d32eb8695b113201f9976ace0cb261b0de4c3e65e86de12b80f703d503f41eb29a1d44f3fc660284bead92b2c9cfa4b63f442929dd21c1cb57d1f7eca476f2806b63c6703c419317cdf4f18efe87b6ea67fca742d43dc77b838f0d92519380b29aeb6a4391d0c01181c7ecafc67dbc1a62919afb7c6da78e613560e548a317ae45d70e680d4ac79824a56473558bd0f56607f60d19c6f0558e62151b7e07d60a10b0e5b77d18c61f4bda0a5478e8e9d6e3a3fdd223543decd900c1bf679a7fd4e3d0f64e12d11b11902fa300b3f679cdb91c1879f2c2445a9c93163b7f1d874b345796b3e0dfe66759c42875e65bc2f16850664c2773b508e34377453f4b1a27fe6ab3dc75c054a76b8cc6ec89629cfa605be5d9cd94e5974bdf012131faaa7df9c542e0adb2444cda5f203a5256b189d8b41a9e7f4b86a900a05892e1ea1a682a689eba570ab7767d0394ecbbbc32f56161e2e60cce2776ac18bd090020d78451ac86f7063689fa3af68dfefc153b44aae16fb28ff47cd41a321f6f09d568033f53943806ba0f8d06ae87210adb65bfbb0794653438a51b52aa915fa5c10bdf23571f8f3a5592fba7f4c7a8556279d0c72c5eb9e05927320a2ed572395224a6ace58f131f9a05531aa0111bddf8b13f418881033f406266a546a662872e477f48ff2b0250bc8f908558f319a59fabca25c0926a5f258f9ae8982bf4087655faa2b856fa0ed8f15c39a0011d3b79b22381bab09eaf9280d7b68a1889d6223d728267db0b7660ec62ce3d2c082c8353198a96ac84c411d5eec7b56b2f4a2628cc2dc1b2bf7b1059c7cef181bae3cdc0d626c1c9c3a0d4f2f56f0042d551d92f57463be1aaa402f41623d9b612bdacac779e8fbd94fe6cc020e85b2241f10504dbcfba78595e98454919123ae1a62ec8591c2e6dc5ed3c6643fd7da75864118146b4ff94fe3835afea72dfb98cdd6d3659b8e7c067c4635b9b230fad51f1362f5644177fa9a721ba70b0a9ec27d411400d795fda1f59b7ebfb59940ab2441551803f94318509c944e463afc9e30dd1a9406d22c340e92e133acacf0f8c920aa46a16dff02e50238e0f5e9435e2a4e135681b2d4c65264320d01ad72b4320fb3009b73ec80523d79bb234454f40e9b3e40381ae30c160da96d6cde44d5fa5c87a60ac4bfd8ded9561c52059ae826725c02c6d4fb04684dc576f3acb76440e33771f85ef4d620b478ffdb712d374d7132bc9dd45df314541cea11dd3596c76896041cca3d9a100c6188d5b5aebba2e7a066a849cc06e6a2373c737ef7d5e96c57e94bd9537d1e619f3cd837def7d114e1f36e62e6df49685ee955d71c4fd7c38c949663dc5cf4d51192dd8893d98e9d2a712423d93a299c31411d2f40f4e5aeaea0a82cd0a7b7c53fcb1868c053d0b80962f1e3af2c2252610a76331ad58d5dc737c6509c5274dc2c60e6d8d6160d0761d1de0fc52e2b805e414345d22b4384287935654480e38f1c9e2fd815470af6279158ee2d897a4534bf8cb3fb5e4261db86ba8708c2d741446a53291d8cbd2997807d2adb44fc72a08eb10c4fd13a872ab8cc89f05785f8f2d26c26e78689794b2e032ecbe4087cbe9c213c3be632a52d0281526da3ec29e2fdd2782c0e007ac4c3649ffa3ee63c955665f9d1accc77db03b4a004793b79cd672ea71ce42ca246e0d29ee5e901c2aed7f7b06811fe2c062e5096a62e6f52d35499c4</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-surge">      <input class="hbe hbe-input-field hbe-input-field-surge" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-surge" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-surge" data-content="请 输 入 阅 读 密 码.">请 输 入 阅 读 密 码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-surge" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M1200,9c0,0-305.005,0-401.001,0C733,9,675.327,4.969,598,4.969C514.994,4.969,449.336,9,400.333,9C299.666,9,0,9,0,9v43c0,0,299.666,0,400.333,0c49.002,0,114.66,3.484,197.667,3.484c77.327,0,135-3.484,200.999-3.484C894.995,52,1200,52,1200,52V9z"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">本文主要记录近期开发相关的学习计划。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="加密博客" scheme="https://www.techgrow.cn/tags/%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>Linux 使用内存缓存减少磁盘 I/O</title>
    <link href="https://www.techgrow.cn/posts/3d17ec47.html"/>
    <id>https://www.techgrow.cn/posts/3d17ec47.html</id>
    <published>2025-12-30T11:50:21.000Z</published>
    <updated>2025-12-30T11:50:21.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在 Linux 系统中，磁盘 I/O 往往是性能瓶颈。当应用程序频繁读取大文件（例如数据库文件、日志文件、多媒体文件）时，系统可能因为 I/O 等待而变得非常卡顿。为了减少磁盘压力、提升性能，Linux 提供了多种利用内存作为缓存的机制，例如：tmpfs、ramfs、Page Cache（页缓存）。本文将详细介绍这些机制的使用方法及区别，并介绍常用的磁盘 I/O 诊断工具。</p><span id="more"></span><h2 id="磁盘-I-O-诊断工具"><a href="#磁盘-I-O-诊断工具" class="headerlink" title="磁盘 I/O 诊断工具"></a>磁盘 I/O 诊断工具</h2><p>在 Linux 下，查看磁盘的吞吐量（每秒读 / 写的数据量）或者负载（IOPS - 每秒读 / 写的请求数），最常用、最直观的工具有下面几种。</p><table><thead><tr><th>工具</th><th>是否需要安装</th><th>能看具体磁盘</th><th>是否推荐使用</th></tr></thead><tbody><tr><td><code>iostat</code></td><td>需要</td><td>✅</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td><code>iotop</code></td><td>需要</td><td>间接</td><td>⭐⭐⭐⭐</td></tr><tr><td><code>vmstat</code></td><td>不需要</td><td>❌</td><td>⭐⭐⭐</td></tr></tbody></table><h3 id="iostat"><a href="#iostat" class="headerlink" title="iostat"></a>iostat</h3><p><code>iostat</code> 是最快速的磁盘工具（强烈推荐）。</p><h4 id="iostat-安装"><a href="#iostat-安装" class="headerlink" title="iostat 安装"></a>iostat 安装</h4><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword"># CentOS</span> / RHEL</span><br><span class="line">sudo yum install sysstat</span><br><span class="line"><span class="keyword"></span></span><br><span class="line"><span class="keyword"># Debian</span> / Ubuntu</span><br><span class="line">sudo apt install sysstat</span><br></pre></td></tr></tbody></table></figure><h4 id="iostat-使用"><a href="#iostat-使用" class="headerlink" title="iostat 使用"></a>iostat 使用</h4><ul><li><code>iostat</code> 的使用 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iostat<span class="params"> -x</span> 1</span><br></pre></td></tr></tbody></table></figure><ul><li>输出示例如下：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">avg-cpu:  %user   %nice %system %iowait  %steal   %idle</span><br><span class="line">           3.59    0.02    1.32    0.33    0.00   94.74</span><br><span class="line"></span><br><span class="line">Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util</span><br><span class="line">sdc               0.09     0.67   33.60   14.91  2100.28  1028.30   128.99     0.32    6.53    2.01   16.73   0.43   2.09</span><br><span class="line">sdb               0.00     0.00    0.17    0.00     9.39     0.00   110.24     0.00    6.75    6.75    0.00   4.22   0.07</span><br><span class="line">sda               0.03     0.31    5.14    0.97   403.47    30.99   142.36     0.09   15.25   11.69   34.10   4.71   2.88</span><br></pre></td></tr></tbody></table></figure><ul><li>重点关注字段</li></ul><table><thead><tr><th>字段</th><th>含义</th><th>如何判断</th></tr></thead><tbody><tr><td><code>%util</code></td><td>磁盘忙碌时间百分比</td><td><strong>接近 100% = 磁盘 I/O 被打满（最重要）</strong>，<code>%util</code> 最高的磁盘 = 当前 I/O 压力最大的磁盘</td></tr><tr><td><code>await</code></td><td>平均 I/O 等待时间（ms）</td><td>等待时间高 = I/O 排队严重</td></tr><tr><td><code>r/s</code></td><td>每秒读的次数</td><td>读的频率，高表示随机读多</td></tr><tr><td><code>w/s</code></td><td>每秒写的次数</td><td>写的频率，高表示随机写多</td></tr><tr><td><code>rkB/s</code></td><td>每秒读取的数据量</td><td>读吞吐量，顺序读场景通常很高</td></tr><tr><td><code>wkB/s</code></td><td>每秒写入的数据量</td><td>写吞吐量，顺序写场景通常很高</td></tr></tbody></table><ul><li>其他字段说明</li></ul><table><thead><tr><th>字段</th><th>含义</th><th>如何判断</th></tr></thead><tbody><tr><td><code>rrqm/s</code></td><td>每秒被合并的读请求数</td><td>值高说明内核在合并顺序读</td></tr><tr><td><code>wrqm/s</code></td><td>每秒被合并的写请求数</td><td>值高说明顺序写、写合并效果好</td></tr><tr><td><code>avgrq-sz</code></td><td>平均每次 I/O 请求大小（KB）</td><td>值小（如 &lt; 16KB）多为随机 I/O；值大多为顺序 I/O</td></tr><tr><td><code>avgqu-sz</code></td><td>平均 I/O 队列长度</td><td>值大于 1 则说明开始排队；持续偏大 = 磁盘处理不过来</td></tr><tr><td><code>r_await</code></td><td>读请求平均等待时间（ms）</td><td>读延迟高，说明读取受阻（数据库、随机读场景重点看）</td></tr><tr><td><code>w_await</code></td><td>写请求平均等待时间（ms）</td><td>写延迟高，常见于日志、落盘、fsync 场景</td></tr><tr><td><code>svctm</code></td><td>平均 I/O 服务时间（ms）</td><td>单次 I/O 被磁盘处理的时间（老指标，仅作参考）</td></tr></tbody></table><ul><li>实战判断口诀<ul><li>磁盘是否被打满？<ul><li>看 <code>%util</code></li></ul></li><li>磁盘为什么慢？<ul><li><code>%util</code> + <code>await</code> + <code>avgqu-sz</code></li></ul></li><li>随机 I/O 还是顺序 I/O？<ul><li><code>avgrq-sz</code> + <code>r/s</code> / <code>w/s</code></li></ul></li><li>是读慢还是写慢？<ul><li><code>r_await</code> vs <code>w_await</code></li></ul></li><li>吞吐瓶颈还是 IOPS 瓶颈？<ul><li><code>rkB/s</code> / <code>wkB/s</code> vs <code>r/s</code> / <code>w/s</code></li></ul></li></ul></li></ul><h3 id="iotop"><a href="#iotop" class="headerlink" title="iotop"></a>iotop</h3><p><code>iotop</code> 是磁盘交互式神器（看进程 + 磁盘）。</p><h4 id="iotop-安装"><a href="#iotop-安装" class="headerlink" title="iotop 安装"></a>iotop 安装</h4><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword"># CentOS</span> / RHEL</span><br><span class="line">sudo yum install iotop</span><br><span class="line"><span class="keyword"></span></span><br><span class="line"><span class="keyword"># Debian</span> / Ubuntu</span><br><span class="line">sudo apt install iotop</span><br></pre></td></tr></tbody></table></figure><h4 id="iotop-使用"><a href="#iotop-使用" class="headerlink" title="iotop 使用"></a>iotop 使用</h4><ul><li><code>iotop</code> 的使用（需要 <code>root</code> 权限）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo iotop</span><br></pre></td></tr></tbody></table></figure><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 或者</span></span><br><span class="line"><span class="comment"># -o 只显示有 I/O 的进程</span></span><br><span class="line"><span class="comment"># -p 显示进程而非线程</span></span><br><span class="line"><span class="comment"># -a 累计 I/O</span></span><br><span class="line">sudo iotop<span class="params"> -oPa</span></span><br></pre></td></tr></tbody></table></figure><ul><li>输出示例如下：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Total DISK READ:  12.34 M/s | Total DISK WRITE: 56.78 M/s</span><br><span class="line">Actual DISK READ: 12.10 M/s | Actual DISK WRITE: 55.90 M/s</span><br><span class="line">  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN   IO&gt;    COMMAND</span><br><span class="line">12345 be/4  mysql    1.20 M/s    35.60 M/s    0.00 %  92.34 %  mysqld</span><br><span class="line">23456 be/4  root     8.50 M/s     0.00 B/s    0.00 %  48.12 %  tar czf backup.tar.gz</span><br><span class="line">34567 be/4  www-data 0.00 B/s     15.80 M/s   0.00 %  61.45 %  php-fpm</span><br><span class="line">  987 be/4  root     0.00 B/s     2.10 M/s    0.00 %   5.23 %  jbd2/sda1-8</span><br></pre></td></tr></tbody></table></figure><ul><li>字段说明</li></ul><table><thead><tr><th>字段</th><th>含义</th><th>说明</th></tr></thead><tbody><tr><td><code>Total DISK READ</code></td><td>总读请求速率</td><td>所有进程发起的磁盘读取速率（总读请求速率）</td></tr><tr><td><code>Total DISK WRITE</code></td><td>总写请求速率</td><td>所有进程发起的磁盘写入速率（总写请求速率）</td></tr><tr><td><code>Actual DISK READ</code></td><td>实际读速率</td><td>实际落盘的磁盘读取速率（受页缓存命中情况影响）</td></tr><tr><td><code>Actual DISK WRITE</code></td><td>实际写速率</td><td>实际落盘的磁盘写入速率（受页缓存、写合并等机制影响）</td></tr><tr><td><code>TID</code></td><td>Thread ID</td><td> 线程 ID（比 PID 更细粒度）</td></tr><tr><td><code>PRIO</code></td><td>I/O 优先级</td><td><code>be</code> = best effort（普通优先级）</td></tr><tr><td><code>USER</code></td><td>进程所属用户</td><td>进程运行的用户</td></tr><tr><td><code>DISK READ</code></td><td>磁盘读取速率</td><td>该进程每秒从磁盘读取的数据量</td></tr><tr><td><code>DISK WRITE</code></td><td>磁盘写入速率</td><td>该进程每秒写入磁盘的数据量</td></tr><tr><td><code>SWAPIN</code></td><td>换页占比</td><td>因内存不足导致的换页 I/O 百分比</td></tr><tr><td><code>IO&gt;</code></td><td>I/O 等待占比</td><td><strong>最重要：进程被磁盘 I/O 阻塞的时间比例</strong></td></tr><tr><td><code>COMMAND</code></td><td>命令名</td><td>进程 / 线程名称</td></tr></tbody></table><ul><li>排查磁盘 I/O 瓶颈时重点关注的字段<ul><li><code>IO&gt;</code>（最重要）<ul><li>举例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">IO&gt;  92.34 %</span><br></pre></td></tr></tbody></table></figure></li><li>含义：<ul><li>进程 92% 的时间在等磁盘 I/O</li><li> 几乎可以确定：磁盘是瓶颈</li></ul></li><li>判断规则：<ul><li><code>IO&gt; &gt; 30%</code> → I/O 明显慢</li><li><code>IO&gt; &gt; 60%</code> → 严重 I/O 阻塞</li><li><code>IO&gt; &gt; 80%</code> → 磁盘被打爆</li></ul></li></ul></li><li><code>DISK READ / DISK WRITE</code><ul><li>举例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DISK WRITE 35.60 M/s</span><br></pre></td></tr></tbody></table></figure></li><li>用来判断：<ul><li>磁盘是读取多，还是写入多</li><li>磁盘是否有异常的大流量进程</li></ul></li><li>特别注意：<ul><li>磁盘吞吐量高 ≠ 磁盘一定慢</li><li>必须结合 <code>IO&gt;</code> 来判断才有意义</li></ul></li></ul></li><li><code>Total / Actual</code> 顶部汇总行（系统级）<ul><li>举例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Total DISK READ:  12.34 M/s | Total DISK WRITE: 56.78 M/s</span><br><span class="line">Actual DISK READ: 12.10 M/s | Actual DISK WRITE: 55.90 M/s</span><br></pre></td></tr></tbody></table></figure></li><li>字段：<ul><li><code>Total DISK READ</code>：所有进程发起的磁盘读取速率（总读请求速率）</li><li><code>Total DISK WRITE</code>：所有进程发起的磁盘写入速率（总写请求速率）</li><li><code>Actual DISK READ</code>：实际落盘的磁盘读取速率（受页缓存命中情况影响）</li><li><code>Actual DISK WRITE</code>：实际落盘的磁盘写入速率（受页缓存、写合并等机制影响）</li></ul></li><li><code>Total</code> 与 <code>Actual</code> 两者差距大的可能原因<ul><li> Page Cache（页缓存）命中高</li><li>写请求被缓存 / 合并</li></ul></li></ul></li></ul></li></ul><h3 id="vmstat"><a href="#vmstat" class="headerlink" title="vmstat"></a>vmstat</h3><p><code>vmstat</code> 无需安装，缺点只能看整体的磁盘 I/O 吞吐量，看不到具体的磁盘。</p><h4 id="vmstat-概述"><a href="#vmstat-概述" class="headerlink" title="vmstat 概述"></a>vmstat 概述</h4><ul><li><code>vmstat</code> 适合用来判断的事情</li></ul><table><thead><tr><th>适用场景</th><th>用法</th></tr></thead><tbody><tr><td>是否有磁盘读写行为</td><td>看 <code>bi</code> / <code>bo</code> 是否为 0</td></tr><tr><td> 是否突然爆发 I/O</td><td> 看是否瞬间飙升</td></tr><tr><td>写入型 / 读取型的吞吐量</td><td>对比 <code>bi</code> vs <code>bo</code></td></tr></tbody></table><ul><li><code>vmstat</code> 不适合用来判断的事情</li></ul><table><thead><tr><th>不适用场景</th><th>原因</th></tr></thead><tbody><tr><td>哪块磁盘 I/O 最高</td><td>看不到具体磁盘</td></tr><tr><td>磁盘是否打满</td><td>没有 <code>%util</code></td></tr><tr><td>I/O 是否排队</td><td>没有 <code>await</code></td></tr></tbody></table><h4 id="vmstat-使用"><a href="#vmstat-使用" class="headerlink" title="vmstat 使用"></a>vmstat 使用</h4><ul><li><code>vmstat</code> 的使用 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vmstat 1</span><br></pre></td></tr></tbody></table></figure><ul><li>输出示例如下：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----</span><br><span class="line"> r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st</span><br><span class="line"> 1  0      0 24642772 6851660 22099404    0    0    50    16  133   71  3  1 95  0  0</span><br><span class="line"> 0  0      0 24644776 6851660 22096364    0    0     0    37 10080 9298  1  0 98  0  0</span><br><span class="line"> 0  0      0 24644308 6851660 22096384    0    0     0   409 3063 8706  1  0 99  0  0</span><br><span class="line"> 0  0      0 24652476 6851660 22096388    0    0     0   226 3145 8780  1  0 99  0  0</span><br><span class="line"> 0  0      0 24640804 6851660 22096400    0    0     0     0 4060 10007  1  1 98  0  0</span><br></pre></td></tr></tbody></table></figure><ul><li><p>重点关注字段</p><ul><li><code>bi</code>（Block In）<ul><li>每秒从块设备读取的数据量（磁盘读流量），单位：KB/s</li><li> 不是针对某一块磁盘，而是所有块设备的读吞吐量总和</li><li><code>bi</code> 高，表示系统正在大量从磁盘读取数据</li></ul></li><li><code>bo</code>（Block Out）<ul><li>每秒写入块设备的数据量（磁盘写流量），单位：KB/s</li><li> 不是针对某一块磁盘，而是所有块设备的写吞吐量总和</li><li><code>bo</code> 高，表现系统正在大量向磁盘写数据</li></ul></li></ul></li><li><p>使用注意事项</p><ul><li><code>vmstat</code> 关注的是「磁盘吞吐量」，而不是「磁盘负载」</li><li><code>bi / bo</code> 高 ≠ 磁盘一定忙</li><li>磁盘顺序读写时：<ul><li><code>bi / bo</code> 很高</li><li>但磁盘可能很轻松</li></ul></li><li>磁盘随机读写时：<ul><li><code>bi / bo</code> 不高</li><li>磁盘却可能被打满</li></ul></li><li>不能用 <code>vmstat</code> 来判断磁盘 I/O 瓶颈<ul><li><code>bi / bo</code> 是全系统汇总值</li><li>只能判断 “有没有磁盘读写”</li><li> 不能判断 “哪个磁盘最忙”</li><li> 不能判断 “某个磁盘是否打满”</li></ul></li></ul></li></ul><h2 id="磁盘-I-O-优化方案"><a href="#磁盘-I-O-优化方案" class="headerlink" title="磁盘 I/O 优化方案"></a>磁盘 I/O 优化方案</h2><h3 id="tmpfs"><a href="#tmpfs" class="headerlink" title="tmpfs"></a>tmpfs</h3><h4 id="tmpfs-的概述"><a href="#tmpfs-的概述" class="headerlink" title="tmpfs 的概述"></a>tmpfs 的概述</h4><ul><li><p><code>tmpfs</code> 是一种基于内存的文件系统：</p><ul><li>文件实际存储在内存 + 交换分区（Swap）中</li><li>支持容量限制（挂载时可指定大小）</li><li>内存不足时，tmpfs 文件可以被交换到 Swap</li><li>tmpfs 的文件内容本质上就是 Page Cache</li><li> 不会导致 OOM（Out of Memory）</li><li>适合存放临时文件、缓存数据</li><li>典型的挂载点：<code>/dev/shm</code>、<code>/run</code></li></ul></li><li><p><code>tmpfs</code> 的主要特点</p><ul><li>支持创建目录（无限层级）</li><li>支持普通文件</li><li>支持符号链接、硬链接</li><li>支持权限、UID/GID</li><li> 支持文件删除（立即释放内存）</li><li>支持调整大小（动态扩缩）</li><li><strong>系统重启后会丢失所有数据</strong></li></ul></li><li><p><code>tmpfs</code> 的适用场景</p><ul><li>程序临时文件</li><li>加速频繁读写的小文件</li><li>构建系统（<code>make</code>、<code>npm</code>、<code>cargo</code> 等）</li><li>OBS、FFmpeg 等读取多媒体文件减少磁盘压力</li></ul></li></ul><h4 id="tmpfs-的使用"><a href="#tmpfs-的使用" class="headerlink" title="tmpfs 的使用"></a>tmpfs 的使用</h4><ul><li>创建挂载点 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir<span class="params"> -p</span> /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>挂载 tmpfs</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 比如限制 4GB 容量</span></span><br><span class="line">sudo mount<span class="params"> -t</span> tmpfs<span class="params"> -o</span> size=4G tmpfs /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>验证挂载 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df<span class="params"> -h</span> /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>开机自动挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 fstab 系统配置文件，添加以下配置内容</span></span><br><span class="line">sudo vim /etc/fstab</span><br><span class="line"></span><br><span class="line"><span class="comment"># 挂载 fstab 中所有自动挂载的文件系统，并检测配置是否正确</span></span><br><span class="line">sudo mount<span class="params"> -a</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmpfs /mnt/ramdisk tmpfs defaults,size=4G 0 0</span><br></pre></td></tr></tbody></table></figure><ul><li>卸载挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo umount /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><h3 id="ramfs"><a href="#ramfs" class="headerlink" title="ramfs"></a>ramfs</h3><h4 id="ramfs-的概述"><a href="#ramfs-的概述" class="headerlink" title="ramfs 的概述"></a>ramfs 的概述</h4><ul><li><p><code>ramfs</code> 是最简单的内存文件系统：</p><ul><li>数据存储在纯内存中</li><li>没有容量限制</li><li>内存写满后系统不会阻止继续写入，会持续占用内存</li><li>内存耗尽就会 OOM，最终导致系统宕机</li><li>更像一个危险，但读写速度极快的文件系统</li></ul></li><li><p><code>ramfs</code> 的主要特点</p><ul><li>支持创建目录（无限层级）</li><li>支持普通文件</li><li>支持符号链接、硬链接</li><li>支持权限、UID/GID</li><li> 支持文件删除（立即释放内存）</li><li>不支持大小限制（不能设置 Size，容量可无限增长）</li><li>数据页不可回收（不会被内核回收）</li><li>不支持 Swap Out（永远驻留物理内存中）</li><li>容易导致系统 OOM（写入越多，内存占用越多）</li><li><strong>系统重启后会丢失所有数据</strong></li></ul></li><li><p><code>ramfs</code> 的适用场景</p><ul><li>极端场景下的高性能缓存</li><li>只用于开发 / 研究，不建议生产环境使用</li></ul></li></ul><h4 id="ramfs-的使用"><a href="#ramfs-的使用" class="headerlink" title="ramfs 的使用"></a>ramfs 的使用</h4><ul><li>创建挂载点 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir<span class="params"> -p</span> /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>挂载 ramfs</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mount<span class="params"> -t</span> ramfs ramfs /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>开机自动挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 fstab 系统配置文件，添加以下配置内容</span></span><br><span class="line">sudo vim /etc/fstab</span><br><span class="line"></span><br><span class="line"><span class="comment"># 挂载 fstab 中所有自动挂载的文件系统，并检测配置是否正确</span></span><br><span class="line">sudo mount<span class="params"> -a</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ramfs   /mnt/ramdisk   ramfs   defaults   0   0</span><br></pre></td></tr></tbody></table></figure><ul><li>卸载挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo umount /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>由于 ramfs 没有容量限制，如果程序写入大量数据会导致系统 OOM。</p></div><h3 id="Page-Cache"><a href="#Page-Cache" class="headerlink" title="Page Cache"></a>Page Cache</h3><h4 id="Page-Cache-的概述"><a href="#Page-Cache-的概述" class="headerlink" title="Page Cache 的概述"></a>Page Cache 的概述</h4><p>即使用户不主动使用 tmpfs 或 ramfs 文件系统，Linux 内核本身也会自动利用内存作为 “磁盘缓存”。当用户读取一个文件时：</p><ul><li>内核将数据加载到操作系统的 Page Cache（页缓存）</li><li>下次读取同一个文件时，不再触发磁盘 I/O，直接从内存返回结果</li></ul><h4 id="Page-Cache-的使用"><a href="#Page-Cache-的使用" class="headerlink" title="Page Cache 的使用"></a>Page Cache 的使用</h4><ul><li>将文件写入 Page Cache</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat video.mp4 &gt; /dev/null</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">上述命令会：</span><br><span class="line">- 强制把 `video.mp4` 文件从磁盘加载进内存</span><br><span class="line">- 但不输出任何内容</span><br><span class="line">- 后续程序读取 `video.mp4` 文件会命中缓存，不再产生磁盘 I/O</span><br></pre></td></tr></tbody></table></figure><ul><li>查看缓存使用情况 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看内存使用情况，会看到 buff/cache 部分增大</span></span><br><span class="line">free<span class="params"> -h</span></span><br></pre></td></tr></tbody></table></figure><ul><li>清除缓存（<strong>慎用，仅测试使用</strong>）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将所有文件系统的缓存数据写回磁盘</span></span><br><span class="line">sudo sync</span><br><span class="line"></span><br><span class="line"><span class="comment"># 清理页缓存、目录缓存和 inode 缓存</span></span><br><span class="line">sudo sh<span class="params"> -c</span> <span class="string">'echo 3 &gt; /proc/sys/vm/drop_caches'</span></span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 Linux 生产环境中，不建议随意清除缓存，否则会影响系统性能。</p></div><h3 id="优化方案总结"><a href="#优化方案总结" class="headerlink" title="优化方案总结"></a>优化方案总结</h3><ul><li><code>tmpfs</code>、<code>ramfs</code>、Page Cache 的优缺点</li></ul><table><thead><tr><th>技术</th><th>优点</th><th>缺点</th><th>适用场景</th></tr></thead><tbody><tr><td> tmpfs</td><td> 快、可限制容量、安全</td><td>占用系统内存</td><td>缓存文件、临时空间、多媒体文件读取</td></tr><tr><td> ramfs</td><td> 极快</td><td>不限容量，OOM 风险高</td><td>测试、高速缓存（不推荐生产环境使用）</td></tr><tr><td>Page Cache</td><td> 自动、透明、无需修改程序</td><td>缓存不可控、可能被挤掉</td><td>文件预加载（热加载）</td></tr></tbody></table><ul><li><code>tmpfs</code> 与 <code>ramfs</code> 的主要区别</li></ul><table><thead><tr><th>特性</th><th> tmpfs</th><th>ramfs</th></tr></thead><tbody><tr><td> 存储位置</td><td>内存 + Swap</td><td> 纯内存（永远不使用 Swap）</td></tr><tr><td>内存限制</td><td>可限制大小（推荐）</td><td>不可限制，体积可无限增长</td></tr><tr><td>系统内存压力大时</td><td>会将部分数据 Swap 出去</td><td>继续占用内存，最终导致系统 OOM</td></tr><tr><td> 默认缓存行为</td><td>文件内容存放在 Page Cache 中，这些页是可回收的，内核在内存压力大时可以回收或 Swap Out</td><td> 文件内容同样存放在 Page Cache 中，但被标记为不可回收，不会 Swap Out，也不会被回收，因此会永久占用物理内存</td></tr><tr><td> OOM 风险</td><td>低（可回收）</td><td>极高（不可回收）</td></tr><tr><td>性能</td><td>与 ramfs 速度差异极小，几乎相同</td><td>比如 tmpfs 略快，但差距可以忽略</td></tr><tr><td>适用场景</td><td>安全缓存、高性能临时存储</td><td>特殊场景、嵌入式、需要可预测行为</td></tr></tbody></table>]]></content>
    
    
    <summary type="html">本文主要介绍 Linux 使用内存缓存减少磁盘 I/O，包括 tmpfs、ramfs、Page Cache 的使用。</summary>
    
    
    
    
    <category term="Linux运维" scheme="https://www.techgrow.cn/tags/Linux%E8%BF%90%E7%BB%B4/"/>
    
    <category term="性能调优" scheme="https://www.techgrow.cn/tags/%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十三</title>
    <link href="https://www.techgrow.cn/posts/ce694f48.html"/>
    <id>https://www.techgrow.cn/posts/ce694f48.html</id>
    <published>2025-12-28T13:55:33.000Z</published>
    <updated>2025-12-28T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a>、<a href="/posts/8c35d945.html">C++ 从入门到精通之十二</a></li><li><a href="/posts/ce694f48.html">C++ 从入门到精通之十三</a>、<a href="/posts/82bb2356.html">C++ 从入门到精通之十四</a></li></ul><span id="more"></span><h2 id="C-并发编程"><a href="#C-并发编程" class="headerlink" title="C++ 并发编程"></a>C++ 并发编程</h2><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/a2a7ad9b.html">C++ 多线程编程之一</a></li></ul></div><h3 id="std-async"><a href="#std-async" class="headerlink" title="std::async"></a>std::async</h3><p>这里不再介绍 <code>std::async</code> 的基础使用，而是重点介绍 <code>std::async</code> 进阶使用的内容。<code>std::async</code> 支持以下三种启动策略：</p><table><thead><tr><th>启动策略</th><th>说明</th></tr></thead><tbody><tr><td><code>std::launch::async</code></td><td>立即执行，创建新线程执行任务。</td></tr><tr><td><code>std::launch::deferred</code></td><td>延迟执行，直到调用 <code>get()</code> 或 <code>wait()</code> 时才在<strong>当前线程（不会创建新线程）</strong>执行。</td></tr><tr><td>默认策略</td><td>由标准库实现决定采用立即执行还是延迟执行。</td></tr></tbody></table><h4 id="std-launch-async"><a href="#std-launch-async" class="headerlink" title="std::launch::async"></a>std::launch::async</h4><p><code>std::launch::async</code> 启动策略会强制异步任务在新的线程中执行，而不是延迟到获取结果时才执行。同时，与该异步任务关联的 <code>std::future</code> 在销毁时，会确保异步任务执行完成，因此即使主线程没有主动调用 <code>std::future</code> 的 <code>get()</code> 或 <code>wait()</code> 函数，程序结束前仍需要等待异步任务执行完成。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第一个参数是启动策略，第二个参数是线程函数，第三个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::async, process, <span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 std::launch::async 执行策略后，会立即创建子线程执行任务，主线程调用 get() 后会阻塞等待任务执行完成并获取执行结果</span></span><br><span class="line">    <span class="comment">// 这里即使不手动调用 get() 或者 wait()，主线程也会等待子线程执行完成后再结束</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"num = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">num = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h4 id="std-launch-deferred"><a href="#std-launch-deferred" class="headerlink" title="std::launch::deferred"></a>std::launch::deferred</h4><p><code>std::thread</code> 创建线程后，如果系统资源紧张，线程创建可能失败并抛出异常；如果异常未被妥善处理，可能进一步导致整个程序异常退出。<strong>因此，在使用 <code>std::thread</code> 时需要额外考虑线程创建失败的情况</strong>。相比之下，<code>std::async</code> 在某些情况下可以根据运行时策略（启动策略）决定是否创建新的线程，从而避免每次调用都显式创建线程。比如，当 <code>std::async</code> 使用 <code>std::launch::deferred</code> 启动策略时，会延迟执行任务，直到调用 <code>std::future</code> 的 <code>get()</code> 或 <code>wait()</code> 时才在当前线程（不会创建新线程）执行任务。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第一个参数是启动策略，第二个参数是线程函数，第三个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::deferred, process, <span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 std::launch::deferred 执行策略后，不会创建新线程，当 get() 被调用后才会在当前主线程（非子线程）开始执行任务</span></span><br><span class="line">    <span class="comment">// 如果 get() 或者 wait() 不被调用，那么任务永远不会执行</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"num = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 1</span><br><span class="line">process() end, current thread id 1</span><br><span class="line">num = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h4 id="自行选择启动策略"><a href="#自行选择启动策略" class="headerlink" title="自行选择启动策略"></a>自行选择启动策略</h4><p><code>std::launch::async | std::launch::deferred</code> 表示两种启动策略都允许使用，由标准库实现自行选择其中一种。因此，<strong>最终启动策略不是固定的</strong>。具体来说：</p><ul><li>如果选择 <code>async</code>：任务会立即异步执行，通常在新线程中执行。</li><li>如果选择 <code>deferred</code>：任务不会立即执行，而是在关联的 <code>std::future</code> 上调用 <code>get()</code> 或 <code>wait()</code> 等等待操作时才执行，并且执行线程通常就是调用等待操作的线程（不会创建新线程）。</li></ul><div class="admonition note"><p class="admonition-title">总结</p><p>当使用 <code>std::launch::async | std::launch::deferred</code> 时，表示允许标准库在异步执行和延迟执行之间进行选择，最终采用哪一种启动策略由标准库实现决定，程序不能假定一定会创建新的线程。这也是 <code>std::async</code> 默认启动策略的行为。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第一个参数是启动策略，第二个参数是线程函数，第三个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::async | std::launch::deferred, process, <span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 上面的两种启动策略任意选择一种（选择的结果是不确定的，由标准库实现自行选择）</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"num = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">num = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h4 id="判断使用的启动策略"><a href="#判断使用的启动策略" class="headerlink" title="判断使用的启动策略"></a>判断使用的启动策略</h4><p>在 C++ 中，可以使用 <code>std::future</code> 的 <code>wait_for()</code> 来判断 <code>std::async</code> 使用的是哪种启动策略（<code>std::launch::async</code> 或者 <code>std::launch::deferred</code>）。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 第一个参数是启动策略，第二个参数是线程函数，第三个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::async | std::launch::deferred, process, <span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断 std::async 使用哪种启动策略</span></span><br><span class="line">    <span class="keyword">const</span> std::future_status status = result.<span class="built_in">wait_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">0</span>));</span><br><span class="line">    <span class="keyword">if</span> ( status == std::future_status::deferred ) {</span><br><span class="line">        <span class="comment">// 延迟执行策略</span></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread deferred"</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// 异步执行策略</span></span><br><span class="line">        <span class="keyword">if</span> (status == std::future_status::ready) {</span><br><span class="line">            <span class="comment">// 线程执行完成并返回结果</span></span><br><span class="line">            std::cout &lt;&lt; <span class="string">"thread finish"</span> &lt;&lt; std::endl;</span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            <span class="comment">// 线程执行超时</span></span><br><span class="line">            std::cout &lt;&lt; <span class="string">"thread timeout"</span> &lt;&lt; std::endl;</span><br><span class="line">            <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">thread timeout</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li><code>std::future::wait_for()</code> 可以用于在指定时间内等待异步任务完成，并通过返回值 <code>std::future_status</code> 判断任务当前状态。<strong>需要注意的是，<code>wait_for()</code> 本身不会影响异步任务的执行，也不会取消任务，只是用于检测任务是否在指定时间内完成</strong>。</li><li>当使用 <code>std::async(std::launch::async, ...)</code> 创建异步任务时，即使 <code>wait_for()</code> 返回 <code>timeout</code>，表示任务仍在执行，异步线程也会继续运行。<strong>若对应的 <code>std::future</code> 对象生命周期结束，在析构阶段会等待异步任务执行完成后再退出程序</strong>。因此，<code>wait_for()</code> 适用于超时检测、任务状态轮询等场景，但不能用于终止正在执行的异步任务。</li></ul></div><h4 id="与-std-thread-的区别"><a href="#与-std-thread-的区别" class="headerlink" title="与 std::thread 的区别"></a>与 std::thread 的区别</h4><p><code>std::thread</code> 和 <code>std::async</code> 都可以用于 C++ 的异步执行，但两者的设计目标不同：<code>std::thread</code> 更偏向于直接管理线程，而 <code>std::async</code> 更偏向于管理异步任务及其执行结果。</p><table><thead><tr><th>对比项</th><th><code>std::thread</code></th><th><code>std::async</code></th></tr></thead><tbody><tr><td>核心定位</td><td>线程管理</td><td>异步任务管理</td></tr><tr><td>返回值</td><td>不直接提供返回值</td><td>可以通过 <code>std::future</code> 获取返回值</td></tr><tr><td>异常处理</td><td>线程内部异常不能直接传递给创建线程</td><td>异常会保存到 <code>std::future</code>，获取结果时可以重新抛出异常</td></tr><tr><td>线程创建</td><td>通常直接创建一个新的线程</td><td>可以选择异步执行，也可以延迟执行</td></tr><tr><td>启动策略</td><td>创建线程后立即开始执行</td><td>支持 <code>std::launch::async</code>、<code>std::launch::deferred</code> 执行策略</td></tr><tr><td>生命周期管理</td><td>需要手动 <code>join()</code> 或 <code>detach()</code></td><td>通过 <code>std::future</code> 管理任务结果和等待</td></tr><tr><td>资源不足</td><td>创建线程失败时可能抛出异常</td><td>使用 <code>std::launch::async</code> 执行策略时，线程资源不足同样可能导致任务启动失败</td></tr><tr><td>适用场景</td><td>需要精细控制线程</td><td>更关注任务执行结果</td></tr></tbody></table><blockquote><p>1、<code>std::thread</code> 更关注「线程」</p></blockquote><p><code>std::thread</code> 的核心是创建和管理一个线程。使用 <code>std::thread</code> 时，需要关注线程的生命周期，例如：</p><ul><li>线程什么时候创建；</li><li>线程什么时候结束；</li><li>是否需要 <code>join()</code>；</li><li>是否需要 <code>detach()</code>；</li><li>多线程之间如何同步和通信。</li></ul><p>因此，<code>std::thread</code> 更适合需要对线程本身进行控制的场景。</p><blockquote><p>2、<code>std::async</code> 更关注「任务」</p></blockquote><ul><li><code>std::async</code> 的核心是异步执行一个任务，并获得任务的执行结果。</li><li><code>std::async</code> 通常与 <code>std::future</code> 配合使用：<ul><li>提交任务 → 异步执行 → 返回 <code>std::future</code> → 通过 <code>future</code> 获取结果。</li></ul></li><li>因此，如果一个函数有返回值，并且希望在后台执行完成后获取这个结果，<code>std::async</code> 通常比 <code>std::thread</code> 更方便。</li></ul><blockquote><p>3、<code>std::async</code> 可以控制任务的执行策略</p></blockquote><ul><li><code>std::async</code> 可以指定：<ul><li><code>std::launch::async</code>：要求异步执行任务，通常由新的线程执行。</li><li><code>std::launch::deferred</code>：延迟执行任务，直到需要获取结果时才执行，并且在调用 <code>get()</code> 或 <code>wait()</code> 的线程中执行。</li><li><code>std::launch::async | std::launch::deferred</code>：由标准库实现自行选择执行策略。</li></ul></li><li><code>std::thread</code> 本身没有这种异步 / 延迟执行策略选择机制。</li></ul><blockquote><p>4、<code>std::async</code> 对返回值和异常处理更加方便</p></blockquote><p><code>std::thread</code> 本身没有提供线程函数返回值的机制，通常需要通过：</p><ul><li>共享变量；</li><li><code>std::promise</code>；</li><li>条件变量；</li><li>其他线程间通信机制</li></ul><p>来传递结果；而 <code>std::async</code> 天然与 <code>std::future</code> 配合，可以直接获取任务返回值。异常处理也类似：</p><ul><li><code>std::thread</code> 中线程函数抛出的未捕获异常，会导致程序调用 <code>std::terminate()</code>；</li><li><code>std::async</code> 会将任务中的异常保存到共享状态中，在通过 <code>std::future</code> 获取执行结果时重新抛出异常。</li></ul><blockquote><p>5、线程数量并不是越多越好</p></blockquote><p>无论是 <code>std::thread</code> 还是使用 <code>std::launch::async</code> 的 <code>std::async</code>，都需要消耗系统线程资源。如果短时间内创建大量线程，可能导致：</p><ul><li>创建线程失败；</li><li>系统资源消耗过大；</li><li>线程上下文切换增加；</li><li>程序性能下降。</li></ul><p>因此，对于大量并发任务，通常不应该简单地通过不断创建 <code>std::thread</code> 或 <code>std::async</code> 来解决，而应该考虑线程池、任务队列等机制。</p><div class="admonition note"><p class="admonition-title">总结</p><ul><li>在 C++ 中，<code>std::thread</code> 是对线程进行管理，<code>std::async</code> 是对异步任务进行管理。</li><li>如果关注的是线程本身的创建、生命周期以及线程控制，可以优先考虑使用 <code>std::thread</code>。</li><li>如果关注的是任务的异步执行、返回结果、异常处理以及等待任务完成，使用 <code>std::async</code> 通常更加方便。</li><li>从抽象层次来看，<code>std::thread</code> 偏向 <code>线程级别 → 更关注手动管理线程</code>，而 <code>std::async</code> 偏向 <code>任务级别 → 更关注任务执行结果</code>。</li></ul></div><h3 id="std-future"><a href="#std-future" class="headerlink" title="std::future"></a>std::future</h3><p>这里不再介绍 <code>std::future</code> 的基础使用，而是重点介绍 <code>std::future</code> 进阶使用的内容。</p><h4 id="wait-for-的使用"><a href="#wait-for-的使用" class="headerlink" title="wait_for() 的使用"></a>wait_for () 的使用</h4><p><code>std::future::wait_for()</code> 用于等待异步任务在指定时间内完成，它不会获取任务结果，而是阻塞当前线程一段时间，并通过返回值表示任务当前状态。返回值类型为 <code>std::future_status</code>，包含三种状态：<code>ready</code> 表示异步任务已完成，可以通过 <code>get()</code> 获取结果；<code>timeout</code> 表示等待时间结束，但任务仍未完成；<code>deferred</code> 表示任务采用延迟执行策略，需要调用 <code>get()</code> 或 <code>wait()</code> 时才会执行。<code>wait_for()</code> 适合用于超时检测、周期性检查异步任务状态等场景，常与 <code>std::async</code>、<code>std::promise</code>、<code>std::packaged_task</code> 等异步机制配合使用。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 启动一个子线程，第二个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::async, process, <span class="number">5000</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待指定时间</span></span><br><span class="line">    std::future_status status =  result.<span class="built_in">wait_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">2</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断 Future 的状态（任务执行状态）</span></span><br><span class="line">    <span class="keyword">if</span> (status == std::future_status::timeout) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"future timeout"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (status == std::future_status::ready) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"future ready"</span> &lt;&lt; std::endl;</span><br><span class="line">    }<span class="keyword">else</span> <span class="keyword">if</span> (status == std::future_status::deferred) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"future deferred"</span> &lt;&lt; std::endl;</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"future unknow status"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// main 线程执行完成后，程序不会立即结束；因为 std::future 对象析构时，会等待对应的异步任务执行完成</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">future timeout</span><br><span class="line">main() end, thread id 1</span><br><span class="line">process() end, current thread id 2</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li><code>std::future::wait_for()</code> 可以用于在指定时间内等待异步任务完成，并通过返回值 <code>std::future_status</code> 判断任务当前状态。<strong>需要注意的是，<code>wait_for()</code> 本身不会影响异步任务的执行，也不会取消任务，只是用于检测任务是否在指定时间内完成</strong>。</li><li>当使用 <code>std::async(std::launch::async, ...)</code> 创建异步任务时，即使 <code>wait_for()</code> 返回 <code>timeout</code>，表示任务仍在执行，异步线程也会继续运行。<strong>若对应的 <code>std::future</code> 对象生命周期结束，在析构阶段会等待异步任务执行完成后再退出程序</strong>。因此，<code>wait_for()</code> 适用于超时检测、任务状态轮询等场景，但不能用于终止正在执行的异步任务。</li></ul></div><h3 id="std-shared-future"><a href="#std-shared-future" class="headerlink" title="std::shared_future"></a>std::shared_future</h3><h4 id="std-shared-future-的概述"><a href="#std-shared-future-的概述" class="headerlink" title="std::shared_future 的概述"></a>std::shared_future 的概述</h4><ul><li><p>概述：</p><ul><li><code>std::shared_future</code> 是 C++ 11 引入的异步结果共享机制，用于<strong>允许多个线程或多个对象共享同一个异步结果</strong>。</li><li>它与 <code>std::future</code> 类似，都可以获取异步任务的结果，但 <code>std::future</code> 通常只能由一个对象获取结果，而 <code>std::shared_future</code> 支持多个对象重复访问同一个结果。</li></ul></li><li><p>特点：</p><ul><li><strong>异步结果共享</strong>：多个 <code>std::shared_future</code> 对象可以共享同一个异步结果。</li><li><strong>支持重复获取</strong>：可以多次调用 <code>get()</code> 获取相同的结果，而 <code>std::future</code> 的 <code>get()</code> 通常只能调用一次。</li><li><strong>线程间共享结果</strong>：适合将同一个异步结果传递给多个线程。</li><li><strong>支持异常共享</strong>：如果异步任务产生异常，多个 <code>std::shared_future</code> 对象调用 <code>get()</code> 时都可以获得该异常。</li><li><strong>支持拷贝</strong>：<code>std::shared_future</code> 支持拷贝，多个对象可以共享同一个共享状态。</li><li><strong>可由 <code>std::future</code> 转换</strong>：可以通过 <code>std::future::share()</code> 将 <code>std::future</code> 转换为 <code>std::shared_future</code>，转换后原 <code>std::future</code> 不再有效。</li></ul></li><li><p>常用成员函数：</p><ul><li><code>get()</code>：获取异步任务的结果，可以被多次调用。</li><li><code>wait()</code>：等待异步任务完成。</li><li><code>wait_for()</code>：等待指定时间，并返回任务状态。</li><li><code>wait_until()</code>：等待到指定时间点，并返回任务状态。</li><li><code>valid()</code>：判断当前 <code>shared_future</code> 是否关联有效的共享状态。</li></ul></li><li><p>与 <code>std::future</code> 的区别：</p><table><thead><tr><th>组件</th><th>作用</th></tr></thead><tbody><tr><td><code>std::future</code></td><td>独占异步结果，通常只能获取一次结果</td></tr><tr><td><code>std::shared_future</code></td><td>共享异步结果，可以被多个对象、多个线程获取</td></tr></tbody></table></li></ul><div class="admonition note"><p class="admonition-title">总结</p><p><code>std::shared_future</code> 是一种<strong>可共享、可重复获取的异步结果对象</strong>，主要解决 <code>std::future</code> 结果只能由单个对象获取的问题。它适合需要将同一个异步结果提供给多个线程或多个消费者的场景。</p></div><h4 id="std-shared-future-的使用"><a href="#std-shared-future-的使用" class="headerlink" title="std::shared_future 的使用"></a>std::shared_future 的使用</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process2</span><span class="params">(<span class="keyword">const</span> std::shared_future&lt;<span class="keyword">int</span>&gt; &amp;result)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process2() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取其他线程的执行结果</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; result.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process2() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 packaged_task，参数是可调用对象（比如普通函数）</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="keyword">int</span>)</span>&gt; <span class="title">task</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取子线程的执行结果，特别注意：get_future() 必须在 packaged_task 执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = task.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将 future 转换为 shared_future</span></span><br><span class="line">    <span class="comment">// std::shared_future&lt;int&gt; result2(std::move(result));</span></span><br><span class="line">    <span class="function">std::shared_future&lt;<span class="keyword">int</span>&gt; <span class="title">result2</span><span class="params">(result.share())</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程 1（第二个参数是线程函数的参数），子线程会直接执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(std::ref(task), <span class="number">5000</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程 2（第二个参数是线程函数的参数），子线程会直接执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(process2, std::ref(result2))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程 1 执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程 2 执行完成</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取其他线程的执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result2.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process2() start, current thread id 3</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">process2() end, current thread id 3</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h3 id="std-atomic"><a href="#std-atomic" class="headerlink" title="std::atomic"></a>std::atomic</h3><p><strong>原子操作（Atomic Operation）是指在执行过程中不会被任何其他操作中断或干扰的最小、不可分割的操作单元。它要么全部执行成功，要么全部执行失败，不会出现一半执行成功一半执行失败的状态</strong>。在多线程或多进程环境中，原子操作对于保证数据一致性至关重要，因为当多个线程同时访问和修改共享数据时，原子操作能确保某一时刻只有一个线程能完成该操作，从而避免了数据竞争和不一致问题。硬件层面，CPU 提供了如比较并交换（CAS）等指令来直接支持原子操作；软件层面，编程语言和操作系统则通过锁、信号量等机制，或提供专门的原子类型和函数，来确保操作的原子性。原子操作是构建并发程序、无锁数据结构以及各种同步机制的基础基石。</p><h4 id="std-atomic-的概述"><a href="#std-atomic-的概述" class="headerlink" title="std::atomic 的概述"></a>std::atomic 的概述</h4><ul><li><p>概述：</p><ul><li><code>std::atomic</code> 是 C++ 11 引入的原子操作机制，用于对共享数据进行线程安全的原子操作，避免多个线程同时访问共享变量时产生数据竞争。</li><li>它通常用于多线程环境中的计数器、状态标志、共享变量等场景，可以保证对变量的读写操作具有原子性。</li></ul></li><li><p>特点：</p><ul><li>原子操作：对 <code>std::atomic</code> 对象的读写、修改等操作具有原子性，不会被其他线程看到中间状态。</li><li>避免数据竞争：多个线程同时操作同一个 <code>std::atomic</code> 对象时，可以避免因非原子访问导致的数据竞争。</li><li>线程安全：适合在多个线程之间安全地共享和修改简单类型的数据。</li><li>高效轻量：相比使用互斥锁保护简单共享变量，原子操作通常具有更低的开销。</li><li>支持多种操作：支持加载、存储、交换、比较并交换以及算术运算等原子操作。</li><li>支持内存序：可以通过 <code>std::memory_order</code> 指定不同的内存序，控制多线程之间的内存访问顺序。</li></ul></li><li><p>常用成员函数：</p><ul><li><code>load()</code>：原子地读取当前值。</li><li><code>store()</code>：原子地写入新值。</li><li><code>exchange()</code>：原子地替换当前值，并返回替换之前的值。</li><li><code>compare_exchange_weak()</code>：比较当前值与期望值，如果相等则更新为新值；失败时允许出现伪失败。</li><li><code>compare_exchange_strong()</code>：比较当前值与期望值，如果相等则更新为新值，不允许出现伪失败。</li><li><code>fetch_add()</code>：原子地执行加法操作，并返回操作之前的值。</li><li><code>fetch_sub()</code>：原子地执行减法操作，并返回操作之前的值。</li><li><code>fetch_and()</code>：原子地执行按位与操作，并返回操作之前的值。</li><li><code>fetch_or()</code>：原子地执行按位或操作，并返回操作之前的值。</li><li><code>fetch_xor()</code>：原子地执行按位异或操作，并返回操作之前的值。</li></ul></li></ul><h4 id="std-atomic-的使用"><a href="#std-atomic-的使用" class="headerlink" title="std::atomic 的使用"></a>std::atomic 的使用</h4><blockquote><p>C++ 存在线程安全的代码</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义全局变量</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> g_count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000</span>; ++i) {</span><br><span class="line">        g_count++;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 创建线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行完成</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 预期输出结果是：2000000</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"count = "</span> &lt;&lt; g_count &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">count = 1088752</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>上面这段案例代码存在线程安全问题。<code>g_count</code> 是多个线程共享的普通变量，而 <code>g_count++</code> 并不是原子操作，实际上包含读取、加 1 和写回三个步骤。当两个线程同时执行时，可能读取到相同的旧值，导致更新丢失，因此存在数据竞争。更严格地说，在 C++ 内存模型下，数据竞争会导致未定义行为。<code>join()</code> 只能保证主线程等待两个子线程执行完成，并不能解决两个子线程之间对 <code>g_count</code> 的并发访问问题。可以使用 <code>std::atomic&lt;int&gt;</code> 对 <code>g_count</code> 进行原子化，或者使用 <code>std::mutex</code> 对临界区进行加锁，从而保证线程安全。</p></div><blockquote><p>使用 <code>std::atomic</code> 解决线程安全问题</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义全局变量（使用原子类型）</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> std::atomic&lt;<span class="keyword">int</span>&gt; <span class="title">g_count</span><span class="params">(<span class="number">0</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000</span>; ++i) {</span><br><span class="line">        g_count++;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 创建子线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程 1 执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程 2 执行完成</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 预期输出结果是：2000000</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"count = "</span> &lt;&lt; g_count.<span class="built_in">load</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">count = 2000000</span><br></pre></td></tr></tbody></table></figure><h4 id="std-atomic-的注意事项"><a href="#std-atomic-的注意事项" class="headerlink" title="std::atomic 的注意事项"></a>std::atomic 的注意事项</h4><p><code>std::atomic</code> 原子操作主要用于多线程环境下保证共享数据访问的线程安全性。对于整数类型，通常支持自增（<code>++</code>）、自减（<code>--</code>）、加法赋值（<code>+=</code>）、减法赋值（<code>-=</code>）、按位与赋值（<code>&amp;=</code>）、按位或赋值（<code>|=</code>）以及按位异或赋值（<code>^=</code>）等原子运算，但具体支持的操作取决于原子类型和编译器实现，并非所有操作都适用于所有 <code>std::atomic</code> 类型。</p><blockquote><p><code>std::atomic</code> 不支持原子操作的写法（存在线程安全问题）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义全局变量（使用原子类型）</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> std::atomic&lt;<span class="keyword">int</span>&gt; <span class="title">g_count</span><span class="params">(<span class="number">0</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000</span>; ++i) {</span><br><span class="line">        <span class="comment">// 下面这种写法不支持原子操作，存在线程安全问题</span></span><br><span class="line">        g_count = g_count + <span class="number">1</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 创建子线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程 1 执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程 2 执行完成</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 预期输出结果是：2000000</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"count = "</span> &lt;&lt; g_count.<span class="built_in">load</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">count = 1264039</span><br></pre></td></tr></tbody></table></figure><h3 id="Windows-临界区"><a href="#Windows-临界区" class="headerlink" title="Windows 临界区"></a>Windows 临界区</h3><h4 id="Windows-临界区的概述"><a href="#Windows-临界区的概述" class="headerlink" title="Windows 临界区的概述"></a>Windows 临界区的概述</h4><p>Windows 临界区（<code>CRITICAL_SECTION</code>）是 Windows 系统提供的一种线程同步机制，主要用于保护进程内多个线程访问共享数据，避免多个线程同时访问共享资源而产生数据竞争。</p><ul><li><p>核心特点</p><ul><li>进程内使用：只能用于同一个进程中的线程之间进行同步。</li><li>互斥访问：同一时刻通常只有一个线程能够进入临界区，其他线程需要等待。</li><li>轻量级：相比 Windows 内核互斥对象，临界区通常具有更低的开销，适合频繁进行线程同步的场景。</li><li>需要初始化和销毁：使用前需要初始化，使用结束后需要释放相关资源。</li><li>手动加锁和解锁：进入临界区后必须确保最终能够离开，否则可能导致其他线程长期等待甚至死锁。</li><li>不支持跨进程同步：如果需要实现不同进程之间的同步，应使用 Windows 的互斥体（<code>Mutex</code>）等内核同步对象。</li></ul></li><li><p>与 <code>std::mutex</code> 的关系</p><ul><li>从功能上看，Windows 系统的 <code>CRITICAL_SECTION</code> 与 C++ 标准库的 <code>std::mutex</code> 都可以用于实现线程之间的互斥访问。</li><li>主要区别在于：<code>CRITICAL_SECTION</code> 是 Windows 平台相关的 API，而 <code>std::mutex</code> 是 C++ 标准库提供的跨平台同步工具。</li><li>因此，在 Windows 专用程序中可以使用 <code>CRITICAL_SECTION</code>；如果希望代码具有更好的跨平台能力，通常更推荐使用 <code>std::mutex</code>。</li><li>注意：<ul><li><code>CRITICAL_SECTION</code> 支持同一个线程多次进入同一个 Windows 临界区，属于递归锁（可重入锁）。</li><li><strong>但是，<code>std::mutex</code> 不支持递归加锁，同一线程多次调用 <code>lock()</code> 会导致阻塞，因此不具备可重入性</strong>。</li><li>在 C++ 11 中，如果需要支持同一线程重复加锁，应该使用 <code>std::recursive_mutex</code>。</li></ul></li></ul></li><li><p>多次进入同一个临界区</p><ul><li><strong>同一个线程可以多次进入同一个 Windows 临界区，这里类似于 Java 的可重入锁（<code>synchronized</code>、<code>ReentrantLock</code> 等）</strong>。</li><li><strong>Windows 临界区支持递归进入：同一个线程在已经进入临界区的情况下，可以再次调用进入临界区操作，不会发生阻塞</strong>。</li><li>内部通过 “递归计数” 管理：Windows 临界区会记录同一线程进入临界区的次数。</li><li>进入与离开需要匹配：调用多少次进入临界区操作，就需要调用相同次数的离开临界区操作，直到计数归零后，临界区才真正释放。</li><li>注意：如果进入次数与离开次数不匹配，可能导致其他线程长期无法进入临界区，造成线程死锁问题。</li><li>举例：<figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">EnterCriticalSection</span>(&amp;winsec);  <span class="comment">// 第一次进入临界区（加锁）</span></span><br><span class="line"><span class="built_in">EnterCriticalSection</span>(&amp;winsec);  <span class="comment">// 第二次进入临界区（加锁）</span></span><br><span class="line">msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line"><span class="built_in">LeaveCriticalSection</span>(&amp;winsec);  <span class="comment">// 第一次离开临界区（解锁）</span></span><br><span class="line"><span class="built_in">LeaveCriticalSection</span>(&amp;winsec);  <span class="comment">// 第二次离开临界区（解锁）</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><div class="admonition note"><p class="admonition-title">总结</p><p>Windows 临界区是一种轻量级的进程内线程同步机制，用于保证同一时刻只有一个线程可以访问受保护的共享资源。它性能较好，但属于 Windows 系统专用 API，现代 C++ 开发中通常推荐优先考虑使用 <code>std::mutex</code>（互斥量）。<strong>特别注意，Windows 的 <code>CRITICAL_SECTION</code> 和 <code>std::mutex</code> 的一个重要区别：Windows 临界区允许同一线程递归进入（即可重入），而 <code>std::mutex</code> 不允许</strong>。</p></div><h4 id="Windows-临界区的使用"><a href="#Windows-临界区的使用" class="headerlink" title="Windows 临界区的使用"></a>Windows 临界区的使用</h4><h5 id="使用案例一"><a href="#使用案例一" class="headerlink" title="使用案例一"></a>使用案例一</h5><div class="admonition note"><p class="admonition-title">案例背景说明</p><p>在网络游戏服务器的设计中，共享数据的保护是一个典型案例：可以创建两个线程，其中一个线程负责收集玩家的命令并将命令数据写入队列，另一个线程则从队列中取出玩家发来的命令，进行解析并执行玩家所需的动作。值得一提的是，在当前业务场景下建议使用生产者消费者模型来实现，并使用 <code>list</code> 容器作为队列。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;Windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> WINDOWS_CRITICAL_SECTION  <span class="comment">// 标记 Windows 系统环境</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">                <span class="comment">// 加锁</span></span><br><span class="line">                <span class="built_in">EnterCriticalSection</span>(&amp;winsec);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 插入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 解锁</span></span><br><span class="line">                <span class="built_in">LeaveCriticalSection</span>(&amp;winsec);</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">                {</span><br><span class="line">                    <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 插入队列</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">                }</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">                <span class="comment">// 加锁</span></span><br><span class="line">                <span class="built_in">EnterCriticalSection</span>(&amp;winsec);</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="comment">// 解锁</span></span><br><span class="line">                    <span class="built_in">LeaveCriticalSection</span>(&amp;winsec);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 操作队列</span></span><br><span class="line">                <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="comment">// 取出队列元素</span></span><br><span class="line">                    command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                    <span class="comment">// 移除队列元素</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 解锁</span></span><br><span class="line">                <span class="built_in">LeaveCriticalSection</span>(&amp;winsec);</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">else</span> </span></span><br><span class="line">                {</span><br><span class="line">                    <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                    <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    }</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 操作队列</span></span><br><span class="line">                    <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                        <span class="comment">// 取出队列元素</span></span><br><span class="line">                        command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                        <span class="comment">// 移除队列元素</span></span><br><span class="line">                        msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                    }</span><br><span class="line">                }</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">            <span class="built_in">InitializeCriticalSection</span>(&amp;winsec);  <span class="comment">// 初始化临界区</span></span><br><span class="line">        <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">            <span class="built_in">DeleteCriticalSection</span>(&amp;winsec);  <span class="comment">// 删除临界区</span></span><br><span class="line">        <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">        CRITICAL_SECTION winsec;  <span class="comment">// Windows 系统中的临界区，作用非常类似于 C++ 11 中的 std::mutex</span></span><br><span class="line">    <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>Windows 系统的 <code>CRITICAL_SECTION</code> 本身不像 <code>std::lock_guard</code> 那样可以自动管理锁，更类似于 <code>std::mutex</code>，所以使用时加锁后千万不要忘记解锁</strong>。</p></div><h5 id="使用案例二"><a href="#使用案例二" class="headerlink" title="使用案例二"></a>使用案例二</h5><p>Windows 系统的 <code>CRITICAL_SECTION</code> 不支持自动离开临界区（自动解锁），为了实现 <code>std::lock_guard</code> 自动解锁的功能，可以使用以下方式封装一个自定义的 RAII 类（比如 <code>CWinLock</code>）。值得注意的是，<code>CWinLock</code> 支持递归锁（可重入锁）。<code>CWinLock</code> 基于 Windows <code>CRITICAL_SECTION</code> 实现，而 <code>CRITICAL_SECTION</code> <strong>支持同一线程递归进入（可重入）</strong>。因此，同一线程可以递归获取同一个 <code>CWinLock</code>，每次进入都会对应一次离开；只有进入次数降为 0 后，临界区才真正被释放；而不同线程尝试进入同一个临界区时，仍然需要等待当前持锁线程完全退出（释放锁）。</p><div class="admonition note"><p class="admonition-title">递归锁（可重入锁）的概念</p><p>递归锁（可重入锁）是指：同一个线程可以多次获取同一把锁，而不会因为重复加锁导致死锁；但每次加锁都必须对应一次解锁，直到加锁次数归零后，锁才真正释放。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;windows.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> WINDOWS_CRITICAL_SECTION  <span class="comment">// 标记 Windows 系统环境</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span>  WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">    <span class="comment">// RAII 类，用于自动释放 Windows 的临界区，防止忘记释放临界区导致线程死锁问题的发生，类似于 C++ 11 中的 std::lock_guard</span></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">CWinLock</span> {</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="comment">// 构造函数</span></span><br><span class="line">        <span class="built_in">CWinLock</span>(CRITICAL_SECTION * pCritical) : <span class="built_in">m_pCritical</span>(pCritical) {</span><br><span class="line">            <span class="built_in">EnterCriticalSection</span>(pCritical);  <span class="comment">// 进入临界区</span></span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 析构函数</span></span><br><span class="line">        ~<span class="built_in">CWinLock</span>() {</span><br><span class="line">            <span class="built_in">LeaveCriticalSection</span>(m_pCritical);  <span class="comment">// 释放临界区</span></span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 禁止拷贝构造</span></span><br><span class="line">        <span class="built_in">CWinLock</span>(<span class="keyword">const</span> CWinLock &amp;lock) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 禁止拷贝赋值</span></span><br><span class="line">        CWinLock&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> CWinLock &amp;lock) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span>:</span><br><span class="line">        CRITICAL_SECTION* <span class="keyword">const</span> m_pCritical;  <span class="comment">// Windows 临界区</span></span><br><span class="line">    };</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">                {</span><br><span class="line">                    <span class="comment">// 加锁（出了作用域会自动解锁）</span></span><br><span class="line">                    <span class="function">CWinLock <span class="title">lock</span><span class="params">(&amp;winsec)</span></span>;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 插入队列</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">                }</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">                {</span><br><span class="line">                    <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 插入队列</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">                }</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">                {</span><br><span class="line">                    <span class="comment">// 加锁（出了作用域会自动解锁）</span></span><br><span class="line">                    <span class="function">CWinLock <span class="title">lock</span><span class="params">(&amp;winsec)</span></span>;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                    <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    }</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 操作队列</span></span><br><span class="line">                    <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                        <span class="comment">// 取出队列元素</span></span><br><span class="line">                        command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                        <span class="comment">// 移除队列元素</span></span><br><span class="line">                        msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                    }</span><br><span class="line">                }</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">                {</span><br><span class="line">                    <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                    <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    }</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 操作队列</span></span><br><span class="line">                    <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                        <span class="comment">// 取出队列元素</span></span><br><span class="line">                        command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                        <span class="comment">// 移除队列元素</span></span><br><span class="line">                        msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                    }</span><br><span class="line">                }</span><br><span class="line">            <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">            <span class="built_in">InitializeCriticalSection</span>(&amp;winsec);  <span class="comment">// 初始化临界区</span></span><br><span class="line">        <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">            <span class="built_in">DeleteCriticalSection</span>(&amp;winsec);  <span class="comment">// 删除临界区</span></span><br><span class="line">        <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">#<span class="meta-keyword">ifdef</span> WINDOWS_CRITICAL_SECTION</span></span><br><span class="line">        CRITICAL_SECTION winsec;  <span class="comment">// Windows 系统中的临界区，作用非常类似于 C++ 11 中的 std::mutex</span></span><br><span class="line">    <span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">总结</p><p>通过 RAII 封装 <code>CRITICAL_SECTION</code>，在构造时自动进入临界区，在析构时自动释放临界区，从而避免因遗漏 <code>LeaveCriticalSection()</code> 而导致线程死锁。<code>CRITICAL_SECTION</code> 负责临界区本身的生命周期，<code>CWinLock</code> 负责一次加锁 / 解锁操作的 RAII 管理。值得注意的是，不建议在 <code>CWinLock</code> 这个 RAII 类中调用 <code>InitializeCriticalSection()</code> / <code>DeleteCriticalSection()</code>，因为 <code>CWinLock</code> 的职责只是管理一次 <code>Enter/Leave</code>，而不是管理 <code>CRITICAL_SECTION</code> 本身的生命周期。</p></div><h3 id="其他互斥量使用"><a href="#其他互斥量使用" class="headerlink" title="其他互斥量使用"></a>其他互斥量使用</h3><h4 id="std-recursive-mutex"><a href="#std-recursive-mutex" class="headerlink" title="std::recursive_mutex"></a>std::recursive_mutex</h4><h5 id="std-recursive-mutex-的概述"><a href="#std-recursive-mutex-的概述" class="headerlink" title="std::recursive_mutex 的概述"></a>std::recursive_mutex 的概述</h5><ul><li>定义：<code>std::recursive_mutex</code> 是 C++ 11 提供的递归互斥锁，也称为可重入锁。</li><li>特点：同一个线程可以多次获取同一把锁，不会因为重复加锁而导致死锁。</li><li>解锁规则：每次 <code>lock()</code> 都必须对应一次 <code>unlock()</code>，只有加锁次数归零后，锁才真正释放。</li><li>适用场景：适用于类的多个成员函数相互调用或者递归调用，并且这些函数都需要对同一资源加锁的场景。</li><li>注意：如果没有递归加锁的需求，推荐优先使用 <code>std::mutex</code>，因为它的语义更加简单明确。</li></ul><div class="admonition note"><p class="admonition-title">总结</p><p><code>std::recursive_mutex</code> 是支持同一线程多次获取同一把锁的互斥量，每次加锁（<code>lock()</code>）都必须对应一次解锁（<code>unlock()</code>）。</p></div><h5 id="std-recursive-mutex-的使用"><a href="#std-recursive-mutex-的使用" class="headerlink" title="std::recursive_mutex 的使用"></a>std::recursive_mutex 的使用</h5><p>在下面的案例代码中，使用了 <code>std::recursive_mutex</code>，因此同一个线程可以重复获取 <code>m_mutex</code>，不会因为递归加锁而导致线程死锁。特别注意，如果将它换成 <code>std::mutex m_mutex;</code>，递归调用 <code>process()</code> 时，同一个线程再次加锁，就会产生线程死锁。</p><blockquote><p>使用 <code>std::recursive_mutex</code> + <code>std::unique_lock</code></p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">(<span class="keyword">int</span> count)</span> </span>{</span><br><span class="line">        <span class="comment">// 获取锁</span></span><br><span class="line">        <span class="function">std::unique_lock&lt;std::recursive_mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"count = "</span> &lt;&lt; count &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (count &gt; <span class="number">0</span>) {</span><br><span class="line">            <span class="comment">// 递归调用，再次获取同一把锁</span></span><br><span class="line">            <span class="built_in">process</span>(count - <span class="number">1</span>);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 这里不需要手动调用 unlock()，离开作用域后，unique_lock 析构时会自动释放锁</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::recursive_mutex m_mutex;  <span class="comment">// 递归互斥锁</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass obj;</span><br><span class="line"></span><br><span class="line">    obj.<span class="built_in">process</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">count = 3</span><br><span class="line">count = 2</span><br><span class="line">count = 1</span><br><span class="line">count = 0</span><br></pre></td></tr></tbody></table></figure><h4 id="std-timed-mutex"><a href="#std-timed-mutex" class="headerlink" title="std::timed_mutex"></a>std::timed_mutex</h4><h5 id="std-timed-mutex-的概述"><a href="#std-timed-mutex-的概述" class="headerlink" title="std::timed_mutex 的概述"></a>std::timed_mutex 的概述</h5><ul><li>定义：<code>std::timed_mutex</code> 是 C++ 11 提供的带超时功能的互斥锁。</li><li>特点：除了支持普通的加锁、解锁外，还支持尝试加锁并设置等待时间。</li><li>超时机制：如果在指定时间内获取不到锁，可以放弃等待并继续执行其他逻辑，避免线程无限阻塞。</li><li>常用成员函数：<ul><li><code>try_lock_for()</code>：等待指定时间。</li><li><code>try_lock_until()</code>：等待到指定时间点。</li></ul></li><li>适用场景：适用于不希望线程长时间阻塞，需要设置加锁超时时间的场景。</li><li>注意：如果不需要超时控制，通常使用 <code>std::mutex</code> 即可。</li></ul><h5 id="std-timed-mutex-的使用"><a href="#std-timed-mutex-的使用" class="headerlink" title="std::timed_mutex 的使用"></a>std::timed_mutex 的使用</h5><blockquote><p>案例一：<code>std::timed_mutex</code> 使用</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 尝试获取锁，最多等待 3 秒</span></span><br><span class="line">        <span class="keyword">if</span> (m_mutex.<span class="built_in">try_lock_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">3</span>))) {</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"成功获取锁, 线程 ID 是 "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">5</span>));</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            m_mutex.<span class="built_in">unlock</span>();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"获取锁超时, 线程 ID 是 "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::timed_mutex m_mutex;  <span class="comment">// 带超时功能的互斥锁</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass obj;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::process, &amp;obj)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::process, &amp;obj)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行完成</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">成功获取锁, 线程 ID 是 2</span><br><span class="line">获取锁超时, 线程 ID 是 3</span><br></pre></td></tr></tbody></table></figure><blockquote><p>案例二：<code>std::timed_mutex</code> + <code>std::unique_lock</code> 使用</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 先创建 unique_lock，但暂时不加锁</span></span><br><span class="line">        <span class="function">std::unique_lock&lt;std::timed_mutex&gt; <span class="title">lock</span><span class="params">(m_mutex, std::defer_lock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 尝试获取锁，最多等待 3 秒</span></span><br><span class="line">        <span class="keyword">if</span> (lock.<span class="built_in">try_lock_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">3</span>))) {</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"成功获取锁, 线程 ID 是 "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">5</span>));</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 这里不需要手动调用 unlock()，离开作用域后，unique_lock 析构时会自动释放锁</span></span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"获取锁超时, 线程 ID 是 "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::timed_mutex m_mutex;  <span class="comment">// 带超时功能的互斥锁</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass obj;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::process, &amp;obj)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::process, &amp;obj)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行完成</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">成功获取锁, 线程 ID 是 2</span><br><span class="line">获取锁超时, 线程 ID 是 3</span><br></pre></td></tr></tbody></table></figure><h4 id="std-recursive-timed-mutex"><a href="#std-recursive-timed-mutex" class="headerlink" title="std::recursive_timed_mutex"></a>std::recursive_timed_mutex</h4><h5 id="std-recursive-timed-mutex-的概述"><a href="#std-recursive-timed-mutex-的概述" class="headerlink" title="std::recursive_timed_mutex 的概述"></a>std::recursive_timed_mutex 的概述</h5><ul><li>定义：<code>std::recursive_timed_mutex</code> 是 C++ 11 提供的支持递归加锁和超时机制的互斥锁。</li><li>递归加锁：同一个线程可以多次获取同一把锁，不会因为重复加锁而死锁。</li><li>超时机制：支持在指定时间内尝试获取锁，超时后返回失败。</li><li>常用成员函数：<ul><li><code>try_lock_for()</code>：尝试获取锁，最多等待指定时间。</li><li><code>try_lock_until()</code>：尝试获取锁，等待到指定时间点。</li></ul></li><li>解锁规则：每次 <code>lock()</code> 都必须对应一次 <code>unlock()</code>，只有加锁次数归零后，锁才真正释放。</li><li>适用场景：既需要递归加锁，又需要超时控制的场景。</li></ul><div class="admonition note"><p class="admonition-title">总结</p><p><code>std::recursive_timed_mutex</code> = <code>std::recursive_mutex</code> 的递归能力 + <code>std::timed_mutex</code> 的超时能力。</p></div><h5 id="std-recursive-timed-mutex-的使用"><a href="#std-recursive-timed-mutex-的使用" class="headerlink" title="std::recursive_timed_mutex 的使用"></a>std::recursive_timed_mutex 的使用</h5><blockquote><p>使用 <code>std::recursive_timed_mutex</code> + <code>std::unique_lock</code></p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">(<span class="keyword">int</span> count)</span> </span>{</span><br><span class="line">        <span class="comment">// 先创建 unique_lock，但暂时不加锁</span></span><br><span class="line">        <span class="function">std::unique_lock&lt;std::recursive_timed_mutex&gt; <span class="title">lock</span><span class="params">(m_mutex, std::defer_lock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (lock.<span class="built_in">try_lock_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">3</span>))) {</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"成功获取锁, 线程 ID 是 "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">", count = "</span> &lt;&lt; count &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">2</span>));</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (count &gt; <span class="number">0</span>) {</span><br><span class="line">                <span class="comment">// 递归调用，再次获取同一把锁</span></span><br><span class="line">                <span class="built_in">process</span>((count - <span class="number">1</span>));</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 这里不需要手动调用 unlock()，离开作用域后，unique_lock 析构时会自动释放锁</span></span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            std::cout &lt;&lt; <span class="string">"获取锁超时, 线程 ID 是 "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::recursive_timed_mutex m_mutex;  <span class="comment">// 支持递归加锁和超时功能的互斥锁</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass obj;</span><br><span class="line"></span><br><span class="line">    obj.<span class="built_in">process</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">成功获取锁, 线程 ID 是 1, count = 3</span><br><span class="line">成功获取锁, 线程 ID 是 1, count = 2</span><br><span class="line">成功获取锁, 线程 ID 是 1, count = 1</span><br><span class="line">成功获取锁, 线程 ID 是 1, count = 0</span><br></pre></td></tr></tbody></table></figure>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Windows 11 专业版 KMS 命令激活教程</title>
    <link href="https://www.techgrow.cn/posts/6ca05db7.html"/>
    <id>https://www.techgrow.cn/posts/6ca05db7.html</id>
    <published>2025-12-25T13:55:33.000Z</published>
    <updated>2025-12-25T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误, 请重新输入." data-whm="文章校验失败, 但不影响阅读解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="44669a3bfe031a82b1f0741d9447db27eb4bfc2c287884322c5483d3c9e55c2f">4cfab7e4fdfd9afdbf1592f9652b8a199f3cb3b67d053a3a2b7db0e886465ca10dd5417753553decd4afc2f563848bbec165c496b7c4f6f669a163d8bbf4c0c9dd1007308880f552e5a0c1bb0012666302e2ec3dfba5e1f5ea9934ea655c381d6425cb4dff9b8a09eca652ab3b851ab8a381bdac1ea568d948ba8dd46c6da9488480c04a492454201728ecc1e372953f1242e8a61b4322691feffbc3437ea780f63426fe7ccb3e0cddf820b581807a64a7aae014a8ecac090d03948c516484292424e671e6d60c671c144919b5907c7abf863be31c7535520f221e38fcfe9c7c08e2aec17edabe7f8ea581ef18b9c0d3000de266b02ac4cd8113db080b216b72dcf485884e5cb93c0cd31e8320f773fc3895b1d187256f3e0517b8a6c0928c0d03274e34b86292c09f1ad54988e35f17efe2ca396183de1cfa19c7442c181436661d881883e6614fd3275d2b304f69c0aaa66028d298c2dd2a2886c8cb8831dbb9d038732ef0649d2c086ca036ecea16e065ec3d77632f184df5d0ea9a01678581e40dc7a88332870b74891186be0f341343e58fad4d9b4364e057336440e51fad9b79863229e57f640692be06254d5205038a9812b3e467de38ad3b2114e2b9fd1be014042b058f4a768f4ecf55b992f6c476d49c56432c6d3b6e2d6bc4763024746cd7c3b399827bc5e2a36c44048393114be765006fc7b11a7877d9fdd22466503a90d9a9eeffe06bbeab00e224c5966012bcb417abafdbe29a38aa0c67154636ae29f0da45fb2db987615f40f4d12e937ba7733961cfcffb5bce3cdafe872f62a102462e480b43f9c46be2cab02fe7aca30a93a18fc52fe7b88f3f39fabcc56fd4176592c41bb0e31816f51e37f3a5434453b28f033af6b05e1378bbc22ddc7262295b69d3fd6646142dfd903c11312bd0c4eceb864601bb5b43f101452d12c4d3d8aa4de16cdd83e08ea271c0b4d9ff3003a5303ed6a8a6ac4b065c213eda73acda5b9afd229d7946f4f8b6edb7e96190d00ae6c5ddc0e9ed2c0474dfe9d2b75ab4a15ac1562a8e9c7a4cecf804fc195bc87f3e7c8476ee9ecd9e42757f43671c80291ac7fa6c10309c8e9634e9384eba408ab17340931ddaa10b27616d2bfa2cdb7a321e3be91d63b70626a4077eee68b4ac3094dc2a71cb4c9c13d7ab9ceb217ff53e574360bad6f25c191fbcb5c37013b51179e6227244d51cd69fa987104711d19d6e550ebfc6cbf800c1607816eecc28b37055318e9ebdf65aba33d43547a64586e1df132d6d2f21d499eefe15525d94597cd9af5d73aec16b77945afd19eb7fac9ff04f93f91bdbeff5cf</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-surge">      <input class="hbe hbe-input-field hbe-input-field-surge" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-surge" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-surge" data-content="请 输 入 阅 读 密 码.">请 输 入 阅 读 密 码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-surge" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M1200,9c0,0-305.005,0-401.001,0C733,9,675.327,4.969,598,4.969C514.994,4.969,449.336,9,400.333,9C299.666,9,0,9,0,9v43c0,0,299.666,0,400.333,0c49.002,0,114.66,3.484,197.667,3.484c77.327,0,135-3.484,200.999-3.484C894.995,52,1200,52,1200,52V9z"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">本文主要介绍如何通过 KMS 命令激活 Windows 11 专业版系统。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="加密博客" scheme="https://www.techgrow.cn/tags/%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2/"/>
    
    <category term="Windows系统运维" scheme="https://www.techgrow.cn/tags/Windows%E7%B3%BB%E7%BB%9F%E8%BF%90%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十二</title>
    <link href="https://www.techgrow.cn/posts/8c35d945.html"/>
    <id>https://www.techgrow.cn/posts/8c35d945.html</id>
    <published>2025-12-16T13:55:33.000Z</published>
    <updated>2025-12-16T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a>、<a href="/posts/8c35d945.html">C++ 从入门到精通之十二</a></li><li><a href="/posts/ce694f48.html">C++ 从入门到精通之十三</a>、<a href="/posts/82bb2356.html">C++ 从入门到精通之十四</a></li></ul><span id="more"></span><h2 id="C-并发编程"><a href="#C-并发编程" class="headerlink" title="C++ 并发编程"></a>C++ 并发编程</h2><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/a2a7ad9b.html">C++ 多线程编程之一</a></li></ul></div><h3 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h3><h4 id="条件变量的概述"><a href="#条件变量的概述" class="headerlink" title="条件变量的概述"></a>条件变量的概述</h4><p><code>std::condition_variable</code> 是 C++ 标准库中用于线程间同步的一个核心工具，它允许线程之间通过事件通知机制进行协作。</p><ul><li><p><strong>核心作用：</strong></p><ul><li>(1) 阻塞线程：让一个或多个线程在特定条件不满足时进入等待（休眠）状态，从而避免无意义的忙等（Busy-Waiting），释放 CPU 资源。</li><li>(2) 唤醒线程：允许另一个线程在修改共享数据后，通知（唤醒）那些正在等待的线程，让它们重新检查条件并继续执行。</li></ul></li><li><p><strong>核心函数：</strong></p><ul><li>等待函数（阻塞）<ul><li><code>wait(unique_lock&lt;mutex&gt;&amp; lock)</code>：原子性地解锁传入的互斥量并使当前线程进入阻塞状态，直到被 <code>notify</code> 唤醒。被唤醒后，它会重新获取互斥量（加锁）并继续执行（通常需要配合循环检查条件）。</li><li><code>wait(unique_lock&lt;mutex&gt;&amp; lock, Predicate pred)</code>：带谓词（条件检查）的重载。等效于 <code>while (!pred()) wait(lock)</code>，它会自动处理虚假唤醒，只有在条件满足时才真正返回。</li></ul></li><li>通知函数（唤醒）<ul><li><code>notify_one()</code>：唤醒正在等待队列中的任意一个线程（如果有的话）。如果多个线程在等待，系统调度选择一个唤醒。</li><li><code>notify_all()</code>：唤醒所有正在等待的线程。这些线程会竞争互斥量，并依次检查各自的条件。</li></ul></li><li>带超时的等待函数<ul><li><code>wait_for()</code> 和 <code>wait_until()</code>：允许线程等待一段特定时间或等待到某个绝对时间点。如果超时后仍未收到通知，线程会自动醒来继续执行，避免永久阻塞。</li></ul></li></ul></li></ul><div class="admonition warning"><p class="admonition-title">特别注意 - `std::condition_variable` 必须与 `std::unique_lock<std::mutex>`配合使用（不能使用`std::lock_guard`替代），因为等待期间需要原子性地释放锁。-`std::condition_variable`必须使用`while` 循环或谓词检查，因为存在虚假唤醒（Spurious Wakeup），即线程可能在未被通知的情况下醒来，必须重新验证条件是否真正满足。</std::mutex></p></div><h4 id="条件变量的使用"><a href="#条件变量的使用" class="headerlink" title="条件变量的使用"></a>条件变量的使用</h4><p>!!! note 案例背景说明<br>在网络游戏服务器的设计中，共享数据的保护是一个典型案例：可以创建两个线程，其中一个线程负责收集玩家的命令并将命令数据写入队列，另一个线程则从队列中取出玩家发来的命令，进行解析并执行玩家所需的动作。值得一提的是，在当前业务场景下建议使用生产者消费者模型来实现，并使用 <code>list</code> 容器作为队列。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 插入队列</span></span><br><span class="line">            msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            lock.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 唤醒一个正在等待的线程</span></span><br><span class="line">            condtion.<span class="built_in">notify_one</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 加锁</span></span><br><span class="line">        <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 解锁</span></span><br><span class="line">        lock.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 唤醒所有等待线程，让其检测 stop 标记并退出</span></span><br><span class="line">        condtion.<span class="built_in">notify_all</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="comment">// 每轮循环重新初始化</span></span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 等待条件满足</span></span><br><span class="line">            <span class="comment">// 如果 Lambda 表达式返回 true，当前线程会继续往下执行（持有锁）</span></span><br><span class="line">            <span class="comment">// 如果 Lambda 表达式返回 false，那么 wait() 将解锁互斥量（释放锁），并让当前线程阻塞等待直到被唤醒</span></span><br><span class="line">            <span class="comment">// wait(lock, predicate) 内部会循环判断 predicate（断言），可以有效避免虚假唤醒</span></span><br><span class="line">            condtion.<span class="built_in">wait</span>(lock, [<span class="keyword">this</span>]() { <span class="keyword">return</span> stop || !msgRecvQueue.<span class="built_in">empty</span>(); });</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 如果程序停止，并且消息队列已经处理完，则退出线程</span></span><br><span class="line">            <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 操作队列</span></span><br><span class="line">            <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="comment">// 取出队列元素</span></span><br><span class="line">                command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 移除队列元素</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            lock.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">bool</span> stop = <span class="literal">false</span>;                 <span class="comment">// 程序停止标记</span></span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;       <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;      <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::condition_variable condtion;  <span class="comment">// 条件变量</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h3 id="std-async"><a href="#std-async" class="headerlink" title="std::async"></a>std::async</h3><h4 id="std-async-的概述"><a href="#std-async-的概述" class="headerlink" title="std::async 的概述"></a><code>std::async</code> 的概述</h4><ul><li><p>概述：</p><ul><li><code>std::async</code> 是 C++ 11 引入的异步任务启动机制，用于将函数异步执行，并返回一个 <code>std::future</code> 对象，以便获取任务执行结果或等待任务完成。</li></ul></li><li><p>特点：</p><ul><li><strong>简化多线程编程</strong>：无需手动创建和管理 <code>std::thread</code>。</li><li><strong>线程同步调用</strong>：调用 <code>get()</code> 时，如果任务尚未完成，会阻塞当前线程等待执行结果。</li><li><strong>异步结果获取</strong>：支持通过 <code>std::future</code> 从异步任务中获取返回结果。</li><li><strong>一次性读取结果</strong>：<code>std::future</code> 的结果只能通过 <code>get()</code> 获取一次，获取后对象将失效。</li><li><strong>支持异常传播</strong>：任务中抛出的异常会保存在 <code>std::future</code> 中，在调用 <code>get()</code> 时重新抛出异常。</li><li><strong>自动资源管理</strong>：任务结束后线程资源自动回收，无需 <code>join()</code> 或 <code>detach()</code>。</li><li><strong>支持状态查询</strong>：可以通过 <code>wait_for()</code>、<code>wait_until()</code> 判断任务是否完成。</li></ul></li><li><p>启动策略：</p><ul><li><code>std::async</code> 支持三种启动方式：<table><thead><tr><th>启动策略</th><th>说明</th></tr></thead><tbody><tr><td><code>std::launch::async</code></td><td>立即执行，创建新线程执行任务。</td></tr><tr><td><code>std::launch::deferred</code></td><td>延迟执行，直到调用 <code>get()</code> 或 <code>wait()</code> 时才在<strong>当前线程（不会创建新线程）</strong>执行。</td></tr><tr><td>默认策略</td><td>由标准库实现决定采用立即执行还是延迟执行。</td></tr></tbody></table></li></ul></li><li><p>常用成员函数：</p><ul><li><code>get()</code>：获取任务返回值（只能调用一次）。</li><li><code>wait()</code>：等待任务完成。</li><li><code>wait_for()</code>：等待指定时间。</li><li><code>wait_until()</code>：等待直到指定时间点。</li></ul></li><li><p>优点：</p><ul><li>使用简单，代码量少。</li><li>自动管理线程生命周期。</li><li>支持返回值和异常传递。</li><li>适合执行独立的异步计算任务。</li></ul></li><li><p>缺点：</p><ul><li>默认启动策略具有不确定性，可能不会真正创建线程。</li><li>无法直接控制线程属性（如线程名称、优先级等）。</li><li>大量创建异步任务可能带来线程创建开销，不适合高并发场景。</li></ul></li><li><p>适用场景：</p><ul><li>后台计算任务。</li><li>并行执行多个独立任务。</li><li>需要获取任务返回值的异步操作。</li><li>临时性的异步任务，而非长期运行的线程。</li></ul></li></ul><div class="admonition note"><p class="admonition-title">总结</p><p><code>std::async</code> 是 C++ 11 提供的高级异步任务接口，它可以自动创建并管理线程，返回 <code>std::future</code> 用于获取执行结果，是比 <code>std::thread</code> 更易用的异步编程方式。</p></div><h4 id="std-async-的使用"><a href="#std-async-的使用" class="headerlink" title="std::async 的使用"></a><code>std::async</code> 的使用</h4><h5 id="使用案例一"><a href="#使用案例一" class="headerlink" title="使用案例一"></a>使用案例一</h5><ul><li><code>std::async</code> + 普通函数的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 启动一个子线程，第二个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(process, <span class="number">5000</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 获取子线程的执行结果（阻塞等待线程执行完成）</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在上面的 <code>std::async</code> 案例代码中，即使不手动调用 <code>std::future</code> 的 <code>get()</code> 函数，<code>main</code> 线程也会等待子线程执行完成。</p></div><h5 id="使用案例二"><a href="#使用案例二" class="headerlink" title="使用案例二"></a>使用案例二</h5><ul><li><code>std::async</code> + 类成员函数的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">        <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 返回业务处理结果</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 启动一个子线程</span></span><br><span class="line">    MyClass mc;</span><br><span class="line">    <span class="comment">// 第二个参数是对象引用，第三个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(&amp;MyClass::process, &amp;mc, <span class="number">5000</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 获取子线程的执行结果（阻塞等待线程执行完成）</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在上面的 <code>std::async</code> 案例代码中，即使不手动调用 <code>std::future</code> 的 <code>get()</code> 函数，<code>main</code> 线程也会等待子线程执行完成。</p></div><h5 id="使用案例三"><a href="#使用案例三" class="headerlink" title="使用案例三"></a>使用案例三</h5><ul><li><code>std::async</code> + <code>std::launch::deferred</code> 的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">        <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 返回业务处理结果</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    MyClass mc;</span><br><span class="line">    <span class="comment">// 第一个参数是启动策略，第三个参数是对象引用，第四个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::deferred, &amp;MyClass::process, &amp;mc, <span class="number">5000</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 使用 std::launch::deferred 执行策略后，不会创建新线程，当 get() 被调用后才会在当前主线程（非子线程）开始执行任务</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 1</span><br><span class="line">process() end, current thread id 1</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在上面的 <code>std::async</code> 案例代码中，如果不手动调用 <code>std::future</code> 的 <code>get()</code> 函数，<code>main</code> 线程不会执行任务。</p></div><h5 id="使用案例四"><a href="#使用案例四" class="headerlink" title="使用案例四"></a>使用案例四</h5><ul><li><code>std::async</code> + <code>std::launch::async</code> 的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">        <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 返回业务处理结果</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    MyClass mc;</span><br><span class="line">    <span class="comment">// 第一个参数是启动策略，第三个参数是对象引用，第四个参数是线程函数的参数</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = std::<span class="built_in">async</span>(std::launch::async, &amp;MyClass::process, &amp;mc, <span class="number">5000</span>);</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"continue ..."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 使用 std::launch::async 执行策略后，会立即创建子线程执行任务，主线程调用 get() 后会阻塞等待任务执行完成并获取执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">continue ...</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在上面的 <code>std::async</code> 案例代码中，即使不手动调用 <code>std::future</code> 的 <code>get()</code> 函数，<code>main</code> 线程也会等待子线程执行完成。</p></div><h3 id="std-packaged-task"><a href="#std-packaged-task" class="headerlink" title="std::packaged_task"></a>std::packaged_task</h3><h4 id="std-packaged-task-的概述"><a href="#std-packaged-task-的概述" class="headerlink" title="std::packaged_task 的概述"></a>std::packaged_task 的概述</h4><ul><li><p>概述：</p><ul><li><code>std::packaged_task</code> 是 C++ 11 引入的异步任务封装机制，用于将一个可调用对象（普通函数、函数对象、Lambda 表达式等）封装成任务，并将任务执行结果与 <code>std::future</code> 关联起来，以便后续获取返回值或异常信息。</li></ul></li><li><p>特点：</p><ul><li><strong>任务封装管理</strong>：可以将任意可调用对象包装成一个可执行任务，统一管理任务执行过程。</li><li><strong>结果异步获取</strong>：任务执行完成后的返回值会保存到共享状态中，并通过关联的 <code>std::future</code> 获取。</li><li><strong>执行与获取分离</strong>：任务的创建、执行和结果获取可以独立进行，提高任务调度灵活性。</li><li><strong>支持异常传播</strong>：任务执行过程中抛出的异常会被保存起来，并在调用 <code>future::get()</code> 时重新抛出。</li><li><strong>一次执行机制</strong>：一个 <code>std::packaged_task</code> 对象通常只能执行一次，执行完成后任务状态失效。</li><li><strong>支持移动语义</strong>：<code>std::packaged_task</code> 不支持拷贝，只支持移动，适合在线程池、任务队列中传递。</li><li><strong>无需手动同步结果</strong>：内部自动管理任务结果与 <code>std::future</code> 之间的关联。</li></ul></li><li><p>常用成员函数：</p><ul><li><code>get_future()</code>：获取与任务结果关联的 <code>std::future</code> 对象，<strong>通常应该在任务执行前调用</strong>。</li><li><code>operator()</code>：执行封装的任务。</li><li><code>valid()</code>：判断当前 <code>std::packaged_task</code> 是否有效。</li><li><code>reset()</code>：重新绑定共享状态，使任务可以再次执行。</li></ul></li><li><p>与其他异步组件的关系：</p><table><thead><tr><th>组件</th><th>作用</th></tr></thead><tbody><tr><td><code>std::future</code></td><td>获取异步任务执行结果。</td></tr><tr><td><code>std::async</code></td><td>自动启动异步任务，并返回 <code>std::future</code>。</td></tr><tr><td><code>std::packaged_task</code></td><td>封装任务，将任务执行和结果获取分离。</td></tr><tr><td><code>std::promise</code></td><td>手动设置异步结果，通常由一个线程生产结果，另一个线程通过 <code>std::future</code> 获取。</td></tr></tbody></table></li><li><p>优点：</p><ul><li>任务封装和线程管理分离，灵活性更高。</li><li>支持在线程池、任务队列中实现任务调度。</li><li>自动处理返回值和异常传递。</li><li>相比 <code>std::async</code>，可以更精细地控制任务执行方式。</li></ul></li><li><p>缺点：</p><ul><li>使用复杂度高于 <code>std::async</code>。</li><li><strong>需要手动安排任务执行线程，不会自动创建线程</strong>。</li><li>一个任务对象只能执行一次，需要重新执行时必须调用 <code>reset()</code>。</li></ul></li><li><p>适用场景：</p><ul><li>线程池任务封装。</li><li>任务队列异步执行。</li><li>需要将任务提交和任务执行分离的场景。</li><li>多线程环境下需要统一管理异步任务结果的场景。</li></ul></li></ul><div class="admonition note"><p class="admonition-title">总结</p><p><code>std::packaged_task</code> 是 C++ 11 提供的任务封装工具，它将<strong>可调用对象、任务执行和结果获取</strong>进行解耦，通过 <code>std::future</code> 获取执行结果。相比 <code>std::async</code>，它提供了更灵活的任务调度能力，常用于线程池等高级异步编程场景。</p></div><div class="admonition warning"><p class="admonition-title">特别注意</p><p><code>std::packaged_task</code> 的 <code>get_future()</code> 通常应在任务执行前调用，以获取与任务结果关联的 <code>std::future</code> 对象。虽然部分编译器实现（例如 Windows 下的 MSVC）允许在任务执行完成后再调用 <code>get_future()</code>，代码也可能正常运行，但这并不是推荐的使用方式。为了保证代码的跨平台兼容性，并符合异步任务的设计思想，应在执行 <code>packaged_task</code> 前完成 <code>std::future</code> 获取，然后通过 <code>future::get()</code> 获取任务执行结果。</p></div><h4 id="std-packaged-task-的使用"><a href="#std-packaged-task-的使用" class="headerlink" title="std::packaged_task 的使用"></a>std::packaged_task 的使用</h4><h5 id="使用案例一-1"><a href="#使用案例一-1" class="headerlink" title="使用案例一"></a>使用案例一</h5><ul><li><code>std::packaged_task</code> + 普通函数的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">process</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回业务处理结果</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 packaged_task，参数是可调用对象（比如普通函数）</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="keyword">int</span>)</span>&gt; <span class="title">task</span><span class="params">(process)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取子线程的执行结果，特别注意：get_future() 必须在 packaged_task 执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = task.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程（第二个参数是线程函数的参数），子线程会直接执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(std::ref(task), <span class="number">5000</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h5 id="使用案例二-1"><a href="#使用案例二-1" class="headerlink" title="使用案例二"></a>使用案例二</h5><ul><li><code>std::packaged_task</code> + Lambda 表达式的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 packaged_task，参数是可调用对象（比如 Lambda 表达式）</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="keyword">int</span>)</span>&gt; <span class="title">task</span><span class="params">([](<span class="keyword">const</span> <span class="keyword">int</span> milliseconds) -&gt; <span class="keyword">int</span> {</span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process start, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 模拟业务处理耗时</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">const</span> std::chrono::milliseconds ms(milliseconds);</span></span></span><br><span class="line"><span class="params"><span class="function">        std::this_thread::sleep_for(ms);</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process end, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 返回业务处理结果</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">return</span> <span class="number">5</span>;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取子线程的执行结果，特别注意：get_future() 必须在 packaged_task 执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = task.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程（第二个参数是线程函数的参数），子线程会直接执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(std::ref(task), <span class="number">5000</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process start, current thread id 2</span><br><span class="line">process end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h5 id="使用案例三-1"><a href="#使用案例三-1" class="headerlink" title="使用案例三"></a>使用案例三</h5><ul><li><code>std::packaged_task</code> 作为可调用对象直接调用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 packaged_task，参数是可调用对象（比如 Lambda 表达式）</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="keyword">int</span>)</span>&gt; <span class="title">task</span><span class="params">([](<span class="keyword">const</span> <span class="keyword">int</span> milliseconds) -&gt; <span class="keyword">int</span> {</span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process start, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 模拟业务处理耗时</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">const</span> std::chrono::milliseconds ms(milliseconds);</span></span></span><br><span class="line"><span class="params"><span class="function">        std::this_thread::sleep_for(ms);</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process end, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 返回业务处理结果</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">return</span> <span class="number">5</span>;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取执行结果，特别注意：get_future() 必须在 packaged_task 执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = task.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 直接调用 packaged_task，在当前线程执行（相当于普通函数调用），不会创建新线程，参数是可调用对象的参数</span></span><br><span class="line">    <span class="built_in">task</span>(<span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取任务执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process start, current thread id 1</span><br><span class="line">process end, current thread id 1</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h5 id="使用案例四-1"><a href="#使用案例四-1" class="headerlink" title="使用案例四"></a>使用案例四</h5><ul><li><code>std::packaged_task</code> + <code>std::vector</code> 容器的使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 packaged_task，参数是可调用对象（比如 Lambda 表达式）</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="keyword">int</span>)</span>&gt; <span class="title">task</span><span class="params">([](<span class="keyword">const</span> <span class="keyword">int</span> milliseconds) -&gt; <span class="keyword">int</span> {</span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process start, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 模拟业务处理耗时</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">const</span> std::chrono::milliseconds ms(milliseconds);</span></span></span><br><span class="line"><span class="params"><span class="function">        std::this_thread::sleep_for(ms);</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process end, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 返回业务处理结果</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">return</span> <span class="number">5</span>;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建容器</span></span><br><span class="line">    std::vector&lt;std::packaged_task&lt;<span class="keyword">int</span>(<span class="keyword">int</span>)&gt;&gt; tasks;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将 packaged_task 放入容器</span></span><br><span class="line">    tasks.<span class="built_in">push_back</span>(std::<span class="built_in">move</span>(task));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从容器获取 packaged_task</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">auto</span> iter = tasks.<span class="built_in">begin</span>();</span><br><span class="line">    std::packaged_task&lt;<span class="built_in"><span class="keyword">int</span></span>(<span class="keyword">int</span>)&gt; task2 = std::<span class="built_in">move</span>(*iter);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从容器移除 packaged_task，后续不能再使用 iter，否则会出现未定义行为</span></span><br><span class="line">    tasks.<span class="built_in">erase</span>(iter);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取执行结果，特别注意：get_future() 必须在 packaged_task 执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = task2.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 直接调用 packaged_task，在当前线程执行（相当于普通函数调用），不会创建新线程，参数是可调用对象的参数</span></span><br><span class="line">    <span class="built_in">task2</span>(<span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取任务执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process start, current thread id 1</span><br><span class="line">process end, current thread id 1</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h5 id="使用案例五"><a href="#使用案例五" class="headerlink" title="使用案例五"></a>使用案例五</h5><ul><li><code>std::packaged_task</code> + <code>std::queue</code> 实现任务队列（常用于线程池）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建任务队列</span></span><br><span class="line">    std::queue&lt;std::packaged_task&lt;<span class="keyword">int</span>(<span class="keyword">int</span>)&gt;&gt; tasks;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建互斥锁，保护任务队列</span></span><br><span class="line">    std::mutex mutex;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 packaged_task，参数是可调用对象（比如 Lambda 表达式）</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="keyword">int</span>)</span>&gt; <span class="title">task</span><span class="params">([](<span class="keyword">const</span> <span class="keyword">int</span> milliseconds) -&gt; <span class="keyword">int</span> {</span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process start, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 模拟业务处理耗时</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">const</span> std::chrono::milliseconds ms(milliseconds);</span></span></span><br><span class="line"><span class="params"><span class="function">        std::this_thread::sleep_for(ms);</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"process end, current thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 返回业务处理结果</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">return</span> <span class="number">5</span>;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取 future，必须在 packaged_task 执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = task.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    {</span><br><span class="line">        <span class="comment">// 加锁，将任务放入任务队列</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将 packaged_task 放入任务队列</span></span><br><span class="line">        tasks.<span class="built_in">push</span>(std::<span class="built_in">move</span>(task));</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建工作线程，模拟任务消费者</span></span><br><span class="line">    <span class="function">std::thread <span class="title">worker</span><span class="params">([&amp;tasks, &amp;mutex]() {</span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"worker thread start, thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        std::packaged_task&lt;<span class="keyword">int</span>(<span class="keyword">int</span>)&gt; current_task;</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        {</span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="comment">// 加锁，从任务队列获取任务</span></span></span></span><br><span class="line"><span class="params"><span class="function">            std::lock_guard&lt;std::mutex&gt; lock(mutex);</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="comment">// 判断任务队列是否为空</span></span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="keyword">if</span> (!tasks.empty()) {</span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="comment">// 从任务队列获取 packaged_task</span></span></span></span><br><span class="line"><span class="params"><span class="function">                current_task = std::move(tasks.front());</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="comment">// 从队列移除 packaged_task</span></span></span></span><br><span class="line"><span class="params"><span class="function">                tasks.pop();</span></span></span><br><span class="line"><span class="params"><span class="function">            }</span></span></span><br><span class="line"><span class="params"><span class="function">        }</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="comment">// 判断是否成功获取任务</span></span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">if</span> (current_task.valid()) {</span></span></span><br><span class="line"><span class="params"><span class="function">            <span class="comment">// 执行 packaged_task，在当前线程执行，不会创建新的线程，参数是可调用对象的参数</span></span></span></span><br><span class="line"><span class="params"><span class="function">            current_task(<span class="number">5000</span>);</span></span></span><br><span class="line"><span class="params"><span class="function">        }</span></span></span><br><span class="line"><span class="params"><span class="function"></span></span></span><br><span class="line"><span class="params"><span class="function">        std::cout &lt;&lt; <span class="string">"worker thread end, thread id "</span> &lt;&lt; std::this_thread::get_id() &lt;&lt; std::endl;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待工作线程执行完成</span></span><br><span class="line">    worker.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">worker thread start, thread id 2</span><br><span class="line">process start, current thread id 2</span><br><span class="line">process end, current thread id 2</span><br><span class="line">worker thread end, thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h3 id="std-promise"><a href="#std-promise" class="headerlink" title="std::promise"></a>std::promise</h3><h4 id="std-promise-的概述"><a href="#std-promise-的概述" class="headerlink" title="std::promise 的概述"></a><code>std::promise</code> 的概述</h4><ul><li><p>概述：</p><ul><li><code>std::promise</code> 是 C++ 11 引入的异步结果传递机制，用于<strong>在线程之间传递数据或异常</strong>。它与 <code>std::future</code> 配合使用，一个线程通过 <code>std::promise</code> 设置结果，另一个线程通过关联的 <code>std::future</code> 获取结果。</li></ul></li><li><p>特点：</p><ul><li><strong>线程间数据传递</strong>：支持一个线程向另一个线程传递异步执行结果。</li><li><strong>主动设置结果</strong>：与 <code>std::async</code> 和 <code>std::packaged_task</code> 自动保存结果不同，<code>std::promise</code> 需要开发者手动调用接口设置结果。</li><li><strong>支持异常传递</strong>：可以在线程中保存异常信息，并通过 <code>future::get()</code> 在另一线程重新抛出异常。</li><li><strong>一次性赋值</strong>：一个 <code>std::promise</code> 对象只能设置一次结果，重复设置会产生异常。</li><li><strong>与 <code>std::future</code> 配合使用</strong>：通过 <code>get_future()</code> 获取关联的 <code>std::future</code> 对象，实现结果同步。</li><li><strong>支持移动语义</strong>：<code>std::promise</code> 不支持拷贝，只支持移动，适合在线程间转移所有权。</li></ul></li><li><p>常用成员函数：</p><ul><li><code>get_future()</code>：获取与当前 <code>promise</code> 关联的 <code>std::future</code> 对象。</li><li><code>set_value()</code>：设置异步任务的返回结果。</li><li><code>set_exception()</code>：设置异常结果，并传递给等待获取结果的线程。</li><li><code>swap()</code>：交换两个 <code>std::promise</code> 对象的状态。</li></ul></li><li><p>与其他异步组件的关系：</p><table><thead><tr><th>组件</th><th>作用</th></tr></thead><tbody><tr><td><code>std::future</code></td><td>获取异步操作的结果。</td></tr><tr><td><code>std::promise</code></td><td>手动设置异步操作的结果或异常。</td></tr><tr><td><code>std::packaged_task</code></td><td>封装任务，并自动将执行结果保存到 <code>std::future</code>。</td></tr><tr><td><code>std::async</code></td><td>自动启动异步任务，并返回 <code>std::future</code>。</td></tr></tbody></table></li><li><p>优点：</p><ul><li>提供灵活的线程间通信方式。</li><li>可以精确控制结果产生的时机。</li><li>支持结果和异常统一传递。</li><li>适合实现自定义异步任务模型。</li></ul></li><li><p>缺点：</p><ul><li>需要手动管理任务执行和结果设置。</li><li>使用复杂度高于 <code>std::async</code>。</li><li>一个 <code>promise</code> 只能关联一个 <code>future</code>，不适合一对多结果通知场景。</li></ul></li><li><p>适用场景：</p><ul><li>多线程之间传递计算结果。</li><li>线程任务完成通知。</li><li>自定义异步任务框架。</li><li>线程池中任务执行结果返回。</li><li>需要手动控制异步结果产生时机的场景。</li></ul></li></ul><div class="admonition note"><p class="admonition-title">总结</p><p><code>std::promise</code> 是 C++ 11 提供的底层异步通信工具，它负责<strong>生产异步结果</strong>，而 <code>std::future</code> 负责<strong>获取异步结果</strong>。相比 <code>std::async</code> 和 <code>std::packaged_task</code>，<code>std::promise</code> 提供了更灵活的结果控制能力，常用于线程间通信和自定义异步任务实现。</p></div><h4 id="std-promise-的使用"><a href="#std-promise-的使用" class="headerlink" title="std::promise 的使用"></a><code>std::promise</code> 的使用</h4><h5 id="使用案例一-2"><a href="#使用案例一-2" class="headerlink" title="使用案例一"></a>使用案例一</h5><ul><li><code>std::promise</code> + <code>std::future</code> 的简单使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">(std::promise&lt;<span class="keyword">int</span>&gt;&amp; result, <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置业务处理结果</span></span><br><span class="line">    result.<span class="built_in">set_value</span>(<span class="number">5</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 Promise 对象</span></span><br><span class="line">    std::promise&lt;<span class="keyword">int</span>&gt; mpro;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取子线程的执行结果，特别注意：get_future() 应该在子线程执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = mpro.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程，第一个参数是线程函数，第二个参数是 Promise 对象的引用，第三个参数毫秒数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(process, std::ref(mpro), <span class="number">5000</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取子线程的执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure><h5 id="使用案例二-2"><a href="#使用案例二-2" class="headerlink" title="使用案例二"></a>使用案例二</h5><ul><li><code>std::promise</code> + <code>std::future</code> 在不同线程间通信的典型使用方式<ul><li><code>process()</code> 线程负责生产结果，通过 <code>promise.set_value()</code> 设置数据结果；</li><li><code>process2()</code> 线程负责消费结果，通过 <code>future.get()</code> 等待并获取数据结果。</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;future&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process</span><span class="params">(std::promise&lt;<span class="keyword">int</span>&gt;&amp; result, <span class="keyword">int</span> milliseconds)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟业务处理耗时</span></span><br><span class="line">    <span class="keyword">const</span> std::<span class="function">chrono::milliseconds <span class="title">ms</span><span class="params">(milliseconds)</span></span>;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(ms);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置业务处理结果</span></span><br><span class="line">    result.<span class="built_in">set_value</span>(<span class="number">5</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">process2</span><span class="params">(std::future&lt;<span class="keyword">int</span>&gt; result)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process2() start, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞等待任务执行结果</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num = result.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"result = "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"process2() end, current thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() run, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 Promise 对象</span></span><br><span class="line">    std::promise&lt;<span class="keyword">int</span>&gt; mpro;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取执行结果，特别注意：get_future() 应该在子线程执行前调用</span></span><br><span class="line">    std::future&lt;<span class="keyword">int</span>&gt; result = mpro.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程 1，第一个参数是线程函数，第二个参数是 Promise 对象的引用，第三个参数毫秒数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(process, std::ref(mpro), <span class="number">5000</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建子线程 2，第一个参数是线程函数，第二个参数是 Future 对象</span></span><br><span class="line">    <span class="comment">// future 不支持拷贝，通过移动转移所有权</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(process2, std::move(result))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main() end, thread id "</span> &lt;&lt; std::this_thread::<span class="built_in">get_id</span>() &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">main() run, thread id 1</span><br><span class="line">process2() start, current thread id 3</span><br><span class="line">process() start, current thread id 2</span><br><span class="line">process() end, current thread id 2</span><br><span class="line">result = 5</span><br><span class="line">process2() end, current thread id 3</span><br><span class="line">main() end, thread id 1</span><br></pre></td></tr></tbody></table></figure>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十一</title>
    <link href="https://www.techgrow.cn/posts/c961bdb.html"/>
    <id>https://www.techgrow.cn/posts/c961bdb.html</id>
    <published>2025-12-05T13:55:33.000Z</published>
    <updated>2025-12-05T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a>、<a href="/posts/8c35d945.html">C++ 从入门到精通之十二</a></li><li><a href="/posts/ce694f48.html">C++ 从入门到精通之十三</a>、<a href="/posts/82bb2356.html">C++ 从入门到精通之十四</a></li></ul><span id="more"></span><h2 id="C-并发编程"><a href="#C-并发编程" class="headerlink" title="C++ 并发编程"></a>C++ 并发编程</h2><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/a2a7ad9b.html">C++ 多线程编程之一</a></li></ul></div><h3 id="互斥量"><a href="#互斥量" class="headerlink" title="互斥量"></a>互斥量</h3><h4 id="互斥量的概念"><a href="#互斥量的概念" class="headerlink" title="互斥量的概念"></a>互斥量的概念</h4><p>C++ 中的互斥量（<code>std::mutex</code>）是一种用于保护共享数据免受并发访问影响的同步原语。它的核心机制是 “独占锁定”：同一时刻，只有一个线程能成功锁定（lock）互斥量并进入临界区执行代码，其他试图进入的线程必须等待，直到持有锁的线程调用解锁（unlock）释放所有权。这种机制有效防止了数据竞争（Data Race）和未定义行为。C++ 11 标准库将互斥量纳入了线程支持库，并提供了 <code>std::lock_guard</code>、<code>std::unique_lock</code> 等 RAII 包装器，用于自动管理锁的获取与释放，避免因异常或忘记解锁而导致的死锁问题。</p><h4 id="互斥量的使用"><a href="#互斥量的使用" class="headerlink" title="互斥量的使用"></a>互斥量的使用</h4><h5 id="lock-、unlock"><a href="#lock-、unlock" class="headerlink" title="lock()、unlock()"></a>lock()、unlock()</h5><p>互斥量（<code>std::mutex</code>）的基本用法遵循显式的加锁与解锁配对原则：线程必须先调用 <code>lock()</code> 获取互斥量，然后安全地操作共享数据，最后调用 <code>unlock()</code> 释放所有权。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>互斥量（<code>std::mutex</code>）的 <code>lock()</code> 和 <code>unlock()</code> 必须严格成对使用，每次 <code>lock()</code> 对应一次 <code>unlock()</code>，既不能多调用一次 <code>unlock()</code>，也不能少调用一次，否则会导致未定义行为或线程死锁等严重问题。</strong></p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 插入队列</span></span><br><span class="line">            msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 判断程序停止标记</span></span><br><span class="line">            <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="comment">// 解锁</span></span><br><span class="line">                msgRecvQueueMutex.<span class="built_in">unlock</span>();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 操作队列</span></span><br><span class="line">            <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="comment">// 取出队列元素</span></span><br><span class="line">                command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                <span class="comment">// 移除队列元素</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h5 id="std-lock-guard-模板"><a href="#std-lock-guard-模板" class="headerlink" title="std::lock_guard 模板"></a>std::lock_guard 模板</h5><p>C++ 11 标准库将互斥量纳入了线程支持库，并提供了 <code>std::lock_guard</code>、<code>std::unique_lock</code> 等 RAII 包装器，用于自动管理锁的获取与释放，避免因异常或忘记解锁而导致的死锁问题。值得一提的是，<code>std::lock_guard</code> 通过 RAII（资源获取即初始化）机制实现自动加锁和解锁：在构造时，它的构造函数会接收一个互斥量对象并立即调用该互斥量的 <code>lock()</code> 方法完成加锁；在析构时，它的析构函数会自动调用互斥量的 <code>unlock()</code> 方法完成解锁。这样，当 <code>lock_guard</code> 对象被创建时锁就被获取，当该对象离开作用域（无论是正常结束、<code>return</code>、<code>break</code> 还是抛出异常）时，析构函数会被自动触发，从而保证锁一定会被释放，避免了因忘记调用 <code>unlock()</code> 或异常导致死锁的问题。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 插入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 操作队列</span></span><br><span class="line">                <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="comment">// 取出队列元素</span></span><br><span class="line">                    command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                    <span class="comment">// 移除队列元素</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h3 id="线程死锁"><a href="#线程死锁" class="headerlink" title="线程死锁"></a>线程死锁</h3><p>C++ 中的线程死锁是指两个或多个线程在执行过程中，因互相等待对方持有的资源（如互斥锁）而永远阻塞的状态。典型场景是线程 A 持有锁 1 并等待锁 2，而线程 B 持有锁 2 并等待锁 1，双方都无法继续推进。死锁的本质是资源竞争和循环依赖，会导致程序完全停滞，既不会崩溃也不会自动恢复。避免死锁的常见策略包括：按固定顺序获取锁、使用超时机制（如 <code>try_lock()</code>）、采用分层锁或使用 RAII 管理锁的持有时间，从而打破循环等待条件。</p><table><thead><tr><th>死锁的四个必要条件</th><th>代码中的体现</th></tr></thead><tbody><tr><td>互斥</td><td><code>std::mutex</code> 保证同一时间只有一个线程能持有锁</td></tr><tr><td>持有并等待</td><td>线程 1 持有 lock1 等待 lock2；线程 2 持有 lock2 等待 lock1</td></tr><tr><td> 不可抢占</td><td><code>std::mutex</code> 不能被强制释放，只能由持有者主动 unlock（解锁）</td></tr><tr><td>循环等待</td><td>线程 1 → lock1 → lock2，线程 2 → lock2 → lock1，形成环路</td></tr></tbody></table><h4 id="触发线程死锁的案例"><a href="#触发线程死锁的案例" class="headerlink" title="触发线程死锁的案例"></a>触发线程死锁的案例</h4><p>下面这段代码会触发线程死锁，因为<strong>两个线程以相反的顺序获取锁</strong>：线程 1 先锁 lock1 再等待 lock2，线程 2 先锁 lock2 再等待 lock1，导致两个线程各自持有一个锁并等待对方释放另一个锁，形成循环等待，结果谁也无法继续执行，程序永久阻塞。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 2</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 1</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::mutex lock1;  <span class="comment">// 锁 1</span></span><br><span class="line">    std::mutex lock2;  <span class="comment">// 锁 2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::func1, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::func2, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行结束</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行结束</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">thread 1 wait to get locker 1</span><br><span class="line">thread 1 already got locker 1</span><br><span class="line">thread 2 wait to get locker 2</span><br><span class="line">thread 2 already got locker 2</span><br><span class="line">thread 1 wait to get locker 2</span><br><span class="line">thread 2 wait to get locker 1</span><br><span class="line">（程序卡住，永远不会打印后续的 "already got" 消息）</span><br></pre></td></tr></tbody></table></figure><h4 id="解决线程死锁的案例"><a href="#解决线程死锁的案例" class="headerlink" title="解决线程死锁的案例"></a>解决线程死锁的案例</h4><blockquote><p>解决线程死锁的方案</p></blockquote><p>在 C++ 中，解决线程死锁的方案有以下几种：</p><ul><li><p>(1) 破坏互斥条件</p><ul><li>使用读写锁（<code>std::shared_mutex</code>）允许多个读</li><li>使用无锁编程（<code>std::atomic</code>）</li></ul></li><li><p>(2) 破坏持有并等待条件</p><ul><li><code>std::lock()</code> 一次性获取所有锁</li><li>预先分配所有资源后再执行</li></ul></li><li><p> (3) 破坏不可抢占条件</p><ul><li>使用 <code>std::timed_mutex</code> + <code>try_lock_for()</code> 设置超时</li><li>超时后释放已持有锁，重试</li></ul></li><li><p> (4) 破坏循环等待条件</p><ul><li>所有线程以相同顺序获取锁（最常用）</li><li>为锁分配层级，按层级顺序锁定</li></ul></li><li><p> (5) 辅助手段</p><ul><li>最小化临界区代码</li><li>减少锁的数量，合并临界区</li><li>使用 <code>std::lock_guard</code> / <code>std::unique_lock</code> 自动释放锁</li><li>使用死锁检测工具（静态分析）</li></ul></li></ul><table><thead><tr><th>避免死锁的最常见方法</th><th>破坏的条件</th><th>复杂度</th><th>性能</th><th>适用场景</th></tr></thead><tbody><tr><td>统一获取锁的顺序</td><td>循环等待</td><td>低</td><td>高</td><td>锁数量少，顺序明确</td></tr><tr><td>一次性获取所有锁（<code>std::lock()</code>）</td><td>循环等待</td><td>低</td><td>中</td><td>多锁场景，推荐优先使用</td></tr><tr><td>加入超时等待机制（<code>std::timed_mutex</code> + <code>try_lock_for()</code>）</td><td>持有并等待</td><td>高</td><td>低</td><td>实时系统，要求响应性</td></tr></tbody></table><div class="admonition warning"><p class="admonition-title">使用 std::lock () 一次性获取多个锁</p><p>在 C++ 中，<code>std::lock()</code> 能够一次性锁住两个或两个以上的互斥量（<code>std::mutex</code>），至少两个，一个不行。它的核心优势是<strong>从根本上避免了因锁的顺序不一致而导致的死锁风险</strong>。工作原理是：如果其中某个互斥量没锁住，它就会一直等待，直到所有互斥量都锁住才会继续执行；关键是它保证了要么全部锁住，要么全部没锁住；如果只锁住了其中一个互斥量，而另一个互斥量没锁成功，它会立即把已经锁住的互斥量释放掉。</p></div><h5 id="线程死锁解决案例一"><a href="#线程死锁解决案例一" class="headerlink" title="线程死锁解决案例一"></a>线程死锁解决案例一</h5><ul><li>线程死锁解决方案：统一获取锁的顺序 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 2</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 2</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::mutex lock1;  <span class="comment">// 锁 1</span></span><br><span class="line">    std::mutex lock2;  <span class="comment">// 锁 2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::func1, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::func2, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行结束</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行结束</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">thread 1 wait to get locker 1</span><br><span class="line">thread 1 already got locker 1</span><br><span class="line">thread 2 wait to get locker 1</span><br><span class="line">thread 1 wait to get locker 2</span><br><span class="line">thread 1 already got locker 2</span><br><span class="line">thread 1 already released locker 2</span><br><span class="line">thread 1 already released locker 1</span><br><span class="line">thread 2 already got locker 1</span><br><span class="line">thread 2 wait to get locker 2</span><br><span class="line">thread 2 already got locker 2</span><br><span class="line">thread 2 already released locker 2</span><br><span class="line">thread 2 already released locker 1</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h5 id="线程死锁解决案例二"><a href="#线程死锁解决案例二" class="headerlink" title="线程死锁解决案例二"></a>线程死锁解决案例二</h5><div class="admonition warning"><p class="admonition-title">特别注意</p><p>使用 <code>std::lock()</code> 锁住多个互斥量（<code>std::mutex</code>）时，如果不使用 <code>std::lock_guard</code> 或 <code>std::unique_lock</code>，则必须手动调用 <code>std::mutex</code> 的 <code>unlock()</code> 释放锁。强烈建议采用 RAII 方式管理锁，此时需要配合 <code>std::adopt_lock</code> 参数，这个参数的作用是告诉 <code>std::lock_guard</code> 或 <code>std::unique_lock</code>：互斥量已经被 <code>std::lock()</code> 锁住了，不要在尝试构造时加锁，只需在析构时自动解锁即可。这样做不仅能在作用域结束时自动释放锁，避免因忘记解锁而引发的资源泄漏，还能在临界区抛出异常时保证锁被正确释放，从而大幅提升代码的安全性和健壮性，同时减少手动管理带来的出错风险。</p></div><ul><li>线程死锁解决方案：使用 <code>std::lock()</code> 一次性获取所有锁 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 一次性锁住两个互斥量</span></span><br><span class="line">        std::<span class="built_in">lock</span>(lock1, lock2);</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用 std::adopt_lock 表明锁已经持有</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g1</span><span class="params">(lock1, std::adopt_lock)</span></span>;</span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g2</span><span class="params">(lock2, std::adopt_lock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 模拟业务执行耗时（临界区代码）</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// std::lock_guard 析构时会自动释放锁</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 一次性锁住两个互斥量（顺序与 func1 不同也没关系）</span></span><br><span class="line">        std::<span class="built_in">lock</span>(lock1, lock2);</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用 std::adopt_lock 表明锁已经持有</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g1</span><span class="params">(lock1, std::adopt_lock)</span></span>;</span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g2</span><span class="params">(lock2, std::adopt_lock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 模拟业务执行耗时（临界区代码）</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// std::lock_guard 析构时会自动释放锁</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::mutex lock1;  <span class="comment">// 锁 1</span></span><br><span class="line">    std::mutex lock2;  <span class="comment">// 锁 2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::func1, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::func2, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行结束</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行结束</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">thread 1 wait to get locker 1 and locker 2</span><br><span class="line">thread 1 already got locker 1 and locker 2</span><br><span class="line">thread 2 wait to get locker 1 and locker 2</span><br><span class="line">thread 1 already released locker 1 and locker 2</span><br><span class="line">thread 2 already got locker 1 and locker 2</span><br><span class="line">thread 2 already released locker 1 and locker 2</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h3 id="unique-lock"><a href="#unique-lock" class="headerlink" title="unique_lock"></a>unique_lock</h3><p><code>unique_lock</code> 是一个通用互斥锁（<code>std::mutex</code>）包装器，通过 RAII（资源获取即初始化）机制自动管理锁的获取与释放，从根本上避免因异常或返回导致的死锁。与简单的 <code>lock_guard</code> 相比，它提供了延迟加锁、尝试加锁、提前解锁和所有权转移等灵活特性，但这也带来稍大的内存与性能开销。其最关键的专属用途是必须配合 <code>condition_variable</code> 使用，因为 <code>wait()</code> 操作需要动态释放和重新获取锁。简言之，<code>unique_lock</code> 是复杂锁定场景与线程同步通信的核心工具，而在只需基础互斥保护的简单场景下，优先考虑轻量的 <code>lock_guard</code> 或 <code>scoped_lock</code>（C++ 17 开始支持）。</p><h4 id="unique-lock-的核心作用"><a href="#unique-lock-的核心作用" class="headerlink" title="unique_lock 的核心作用"></a>unique_lock 的核心作用</h4><ul><li><p>核心作用：RAII（资源获取即初始化）锁管理</p><ul><li><code>unique_lock</code> 的核心思想是利用对象的构造函数和析构函数，自动管理互斥量的加锁与解锁。<ul><li>构造时：调用 <code>mutex.lock()</code>（或尝试加锁）。</li><li>析构时：自动调用 <code>mutex.unlock()</code>。</li></ul></li><li>这带来了最直接的好处：即使代码在执行过程中抛出异常或提前 <code>return</code>，<code>unique_lock</code> 的析构函数也一定会被执行，从而彻底避免死锁（因为锁一定会被释放）。</li></ul></li><li><p>相较于 <code>lock_guard</code> 的增强（独特优势）</p><ul><li>虽然 <code>lock_guard</code> 也能做到 RAII，但 <code>unique_lock</code> 更加灵活。它弥补了 <code>std::mutex</code> 原生接口和 <code>lock_guard</code> 的不足，提供了以下关键特性：</li><li>延迟加锁（Deferred Locking）：构造时可以不立即加锁，而是稍后在需要时手动调用 <code>lock()</code>。这在需要将锁的创建与加锁分离时非常有用。</li><li>尝试加锁（Try Locking）：支持 <code>try_lock()</code>, <code>try_lock_for()</code>, <code>try_lock_until()</code>，允许线程在尝试获取锁失败时去做其他事情，而不是阻塞等待，提高了程序的响应性。</li><li>所有权转移（Move Semantics）：<code>unique_lock</code> 是可移动的（但不可拷贝）。你可以将锁的所有权从一个对象转移给另一个对象，或者从函数中返回一个已加锁的 <code>unique_lock</code>，这极大地方便了在函数间传递锁的管控权。</li><li>提前解锁（Manual Unlock）：在锁的作用域结束前，可以手动调用 <code>unlock()</code>。这在需要保护一段临界区极短的代码，且后续还有耗时操作时非常有用 —— 提前释放锁以减少锁持有的时间，提高并发度。</li></ul></li></ul><hr><ul><li><p>与条件变量（<code>condition_variable</code>）配合的强制性要求</p><ul><li>这是 <code>unique_lock</code> 最重要的专属职责。C++ 标准规定，<code>condition_variable</code> 的 <code>wait()</code> 系列函数只能接受 <code>unique_lock</code>，不能接受 <code>lock_guard</code>。</li><li>原因在于 <code>wait()</code> 内部需要原子的释放锁并进入阻塞状态，并且在被唤醒后需要重新获取锁。只有 <code>unique_lock</code> 支持这种动态的加锁 / 解锁 / 所有权转移操作，而 <code>lock_guard</code> 过于死板，无法满足这一需求。</li></ul></li><li><p><code>unique_lock</code> 的性能与开销权衡</p><ul><li>需要注意的是，灵活性是有代价的。<code>unique_lock</code> 内部通常需要维护一个标志位来记录当前是否持有锁（以便在析构时决定是否解锁），因此它的内存占用比 <code>lock_guard</code> 稍大，性能开销也稍高。</li><li>如果只需要简单的 RAII 加锁 / 解锁，且不需要上述灵活性：建议使用 <code>lock_guard</code>（或 C++17 的 <code>scoped_lock</code>），它们更轻量且意图更明确。</li><li>如果需要延迟加锁、尝试加锁、移动所有权或配合条件变量：请使用 <code>unique_lock</code>。</li></ul></li></ul><h4 id="unique-lock-的构造参数"><a href="#unique-lock-的构造参数" class="headerlink" title="unique_lock 的构造参数"></a>unique_lock 的构造参数</h4><p>在 C++ 中，<code>unique_lock</code> 的构造函数最常用的参数主要有以下几种：</p><ul><li><p><code>std::adopt_lock_t</code>（接管锁）</p><ul><li>假设当前线程已经成功获取了互斥量的锁（比如使用 <code>std::lock()</code>），<code>unique_lock</code> 在构造时不再尝试加锁，而是直接接管该锁的所有权，并在析构时自动解锁。</li><li>示例：<code>std::unique_lock&lt;std::mutex&gt; lk(mtx, std::adopt_lock);</code></li></ul></li><li><p><code>std::defer_lock_t</code>（延迟加锁）</p><ul><li><code>unique_lock</code> 在构造时不加锁，仅持有互斥量的引用。后续需要时再通过调用 <code>lk.lock()</code> 手动加锁。常用于将对象创建与加锁时机分离。  </li><li>示例：<code>std::unique_lock&lt;std::mutex&gt; lk(mtx, std::defer_lock);</code></li></ul></li><li><p><code>std::try_to_lock_t</code>（尝试加锁）</p><ul><li><code>unique_lock</code> 在构造时调用 <code>mutex.try_lock()</code> 尝试加锁。加锁结果可通过 <code>lk.owns_lock()</code> 判断，即使加锁失败也不会阻塞线程，允许程序做其他处理。  </li><li>示例：<code>std::unique_lock&lt;std::mutex&gt; lk(mtx, std::try_to_lock);</code></li></ul></li><li><p>超时时间（时间点或等待时长）</p><ul><li><code>unique_lock</code> 在构造时尝试加锁，但若无法立即获取，则阻塞等待直至超时。支持相对时长（<code>std::chrono::duration</code>）或绝对时间点（<code>std::chrono::time_point</code>）。</li><li>示例：<ul><li><code>std::unique_lock&lt;std::timed_mutex&gt; lk(mtx, std::chrono::seconds(1));</code>（等待 1 秒）</li><li><code>std::unique_lock&lt;std::timed_mutex&gt; lk(mtx, std::chrono::steady_clock::now() + std::chrono::seconds(1));</code></li></ul></li></ul></li><li><p>默认情况（无参数）：</p><ul><li><code>unique_lock</code> 在构造时立即调用 <code>mutex.lock()</code>，阻塞当前线程直到获取锁为止。</li></ul></li></ul><div class="admonition warning"><p class="admonition-title">特别注意</p><p>使用 <code>unique_lock</code> 时需注意，除默认构造函数外，以上参数必须与互斥量类型（<code>std::mutex</code>）匹配（例如，超时参数要求互斥量支持 <code>try_lock_for</code> 或 <code>try_lock_until</code>，即 <code>std::timed_mutex</code> 等）。</p></div><h4 id="unique-lock-的成员函数"><a href="#unique-lock-的成员函数" class="headerlink" title="unique_lock 的成员函数"></a>unique_lock 的成员函数</h4><p><code>unique_lock</code> 的常用成员函数主要分为锁操作、状态查询和所有权管理三类：</p><ul><li><p>锁操作（加锁 / 解锁）</p><ul><li><code>lock()</code>：手动加锁。若互斥量已被其他线程持有，则阻塞等待，需在未持有锁时调用。</li><li><code>try_lock()</code>：尝试加锁。立即返回，成功返回 <code>true</code>，失败返回 <code>false</code>，不阻塞线程。</li><li><code>try_lock_for(duration)</code>：尝试加锁，等待指定的相对时长（需互斥量支持 <code>try_lock_for()</code>，如 <code>std::timed_mutex</code>）。</li><li><code>try_lock_until(time_point)</code>：尝试加锁，等待直到指定的绝对时间点（需互斥量支持 <code>try_lock_until</code>）。</li><li><code>unlock()</code>：手动解锁。通常用于提前释放锁，减少临界区持有时间，需确保当前线程持有锁。</li></ul></li><li><p>锁状态查询</p><ul><li><code>owns_lock()</code>：返回 <code>bool</code>，表示当前是否持有锁。常用于 <code>try_lock()</code> 系列操作后判断是否成功获取锁。</li><li><code>operator bool()</code>：与 <code>owns_lock()</code> 功能相同，允许在条件判断中直接使用对象（如 <code>if (lk) { ... }</code>）。</li><li><code>mutex()</code>：返回指向所管理互斥量的指针（不常用，一般用于代码调试）。</li></ul></li><li><p>所有权管理</p><ul><li><code>release()</code>：释放所有权，即解除 <code>unique_lock</code> 与互斥量（<code>std::mutex</code>）的关联，但不解锁互斥量。<strong>返回互斥量指针，调用者需负责后续手动解锁</strong>。常用于转移锁管理责任。</li><li>移动语义（非成员函数风格）：<code>unique_lock</code> 可移动（包括移动构造函数和移动赋值运算符），允许将锁的所有权从一个对象转移给另一个对象。</li></ul></li></ul><hr><ul><li>成员函数的使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">std::timed_mutex mtx;</span><br><span class="line"><span class="function">std::unique_lock&lt;std::timed_mutex&gt; <span class="title">lk</span><span class="params">(mtx, std::defer_lock)</span></span>; <span class="comment">// 延迟加锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试获取锁，最多等待 100 毫秒</span></span><br><span class="line"><span class="keyword">if</span> (lk.<span class="built_in">try_lock_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">100</span>))) {</span><br><span class="line">    <span class="comment">// 成功获取锁，操作共享数据</span></span><br><span class="line">    lk.<span class="built_in">unlock</span>();  <span class="comment">// 提前释放锁</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">    <span class="comment">// 超时等待未获得锁，做其他处理</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li><code>unique_lock</code> 的 <code>lock()</code> 和 <code>unlock()</code> 必须成对使用，且调用前需确认当前锁状态，否则可能导致未定义行为（如重复加锁或解锁未持有的锁）。</li><li><code>unique_lock</code> 调用 <code>release()</code> 后，<code>unique_lock</code> 析构时不会再自动解锁，调用者需负责后续手动解锁，使用时需格外谨慎。</li><li><code>unique_lock</code> 在大部分场景下，开发者无需手动调用 <code>unlock()</code>，利用 RAII 自动析构即可；手动解锁主要用于优化性能（提前释放锁）。</li></ul></div><h4 id="unique-lock-的所有权传递"><a href="#unique-lock-的所有权传递" class="headerlink" title="unique_lock 的所有权传递"></a>unique_lock 的所有权传递</h4><p>在 C++ 中，<code>unique_lock</code> 遵循移动语义，其互斥量（<code>std::mutex</code>）的所有权<strong>可以转移，但不能复制</strong>。这意味着一个 <code>unique_lock</code> 对象可以通过移动构造函数或移动赋值运算符将其对互斥量的管理权交给另一个 <code>unique_lock</code> 对象，转移后原 <code>unique_lock</code> 对象不再持有锁，从而避免多个 <code>unique_lock</code> 对象同时管理同一互斥量。这种设计既保证了资源管理的唯一性，又提供了在不同作用域或函数间传递锁所有权的灵活性。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li><code>unique_lock</code> 对象不能被复制：如果试图写 <code>std::unique_lock&lt;std::mutex&gt; lock2 = lock;</code>（拷贝），编译器会报错。</li><li><code>unique_lock</code> 所有权转移后，原 <code>unique_lock</code> 对象会变为空状态（不持有任何锁），其 <code>owns_lock()</code> 返回 <code>false</code>，析构时不会尝试解锁。</li><li><code>unique_lock</code> 所有权转移后，不能再对原 <code>unique_lock</code> 对象调用 <code>lock()</code> 和 <code>unlock()</code> 等方法，否则会导致未定义行为（程序崩溃、运行时抛出异常等）。</li></ul></div><ul><li>案例代码一 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::mutex mx;</span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock1</span><span class="params">(mx)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 非法代码（编译出错），所有权是不允许复制的</span></span><br><span class="line">    <span class="comment">// std::unique_lock&lt;std::mutex&gt; lock2(lock1);</span></span><br><span class="line">    <span class="comment">// std::unique_lock&lt;std::mutex&gt; lock2 = lock1;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 合法代码（编译通过），所有权是允许转移的</span></span><br><span class="line">    <span class="comment">// 所有权转移后，lock1 的互斥量指向为空（不持有任何锁），而 lock2 的互斥量指向为 mx</span></span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock2</span><span class="params">(std::move(lock1))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>案例代码二 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">getLock</span><span class="params">(std::mutex &amp;mx)</span> </span>{</span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(mx)</span></span>;</span><br><span class="line">    <span class="comment">// 函数结果返回 unique_lock 局部对象是可以的，系统会生成临时的 unique_lock 对象，并调用 unique_lock 的移动构造函数</span></span><br><span class="line">    <span class="keyword">return</span> lock;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::mutex mx;</span><br><span class="line">    std::unique_lock&lt;std::mutex&gt; lock = <span class="built_in">getLock</span>(mx);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="unique-lock-的完整使用案例"><a href="#unique-lock-的完整使用案例" class="headerlink" title="unique_lock 的完整使用案例"></a>unique_lock 的完整使用案例</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 插入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 操作队列</span></span><br><span class="line">                <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="comment">// 取出队列元素</span></span><br><span class="line">                    command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                    <span class="comment">// 移除队列元素</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h3 id="单例设计模式"><a href="#单例设计模式" class="headerlink" title="单例设计模式"></a>单例设计模式</h3><p>在 C++ 中使用单例模式时，一般不用管单例对象的内存释放问题，也就是不需要编写自定义的析构函数。</p><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/66192ea1.html">C++ 设计模式使用教程之一</a></li><li><a href="/posts/bcb943df.html">C++ 设计模式使用教程之二</a></li></ul></div><h4 id="单例设计模式实现"><a href="#单例设计模式实现" class="headerlink" title="单例设计模式实现"></a>单例设计模式实现</h4><h5 id="饿汉单例模式"><a href="#饿汉单例模式" class="headerlink" title="饿汉单例模式"></a>饿汉单例模式</h5><p>饿汉单例模式是指还没有获取单例对象，单例对象就已经创建完成（初始化）了。值得一提的是，<strong>饿汉单例模式是线程安全的</strong>，因为单例对象在 <code>main()</code> 函数执行前就已经初始化完成（静态初始化），所以是线程安全的（因为对象已经存在，只是返回指针）。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_instance;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 静态成员变量</span></span><br><span class="line">    <span class="keyword">static</span> MyClass* m_instance;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象），分配内存空间</span></span><br><span class="line">MyClass* MyClass::m_instance = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x55a0fe5b4eb0</span><br><span class="line">address: 0x55a0fe5b4eb0</span><br><span class="line">equals: true</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">资源泄漏问题</p><ul><li>上面饿汉单例模式的代码存在资源泄漏问题，不推荐使用（建议使用静态局部变量实现单例模式），原因如下：</li><li>(1) 对象在堆上分配内存，但从未被 <code>delete</code>，内存不会被释放；</li><li>(2) 由于析构函数是 <code>private</code>，即使开发者想手动 <code>delete</code> 也做不到；</li><li>(3) 当程序退出时，对象的析构函数不会被调用，资源永远无法得到释放。</li></ul></div><h5 id="懒汉单例模式"><a href="#懒汉单例模式" class="headerlink" title="懒汉单例模式"></a>懒汉单例模式</h5><p>懒汉单例模式是指在第一次被使用时才创建单例对象，而不是程序启动时就创建。<strong>特别注意，懒汉单例模式不一定是线程安全的，由具体的代码实现决定。</strong>线程安全的懒汉单例模式有以下几种实现方式：</p><table><thead><tr><th>实现方式</th><th>是否线程安全</th><th>防止指令重排</th><th>推荐指数</th></tr></thead><tbody><tr><td>静态局部变量</td><td>✅（需要 C++ 11 及以上的编译器）</td><td>✅</td><td>⭐⭐⭐⭐（最推荐，最简洁）</td></tr><tr><td><code>mutex</code> + <code>atomic</code> + <code>DCL</code></td><td>✅（兼容 C++ 11 以下的编译器，如 C++ 98 / C++ 03</td><td>✅</td><td>⭐⭐⭐（适合对底层控制有要求的场景）</td></tr></tbody></table><blockquote><p>C++ 基于静态局部变量实现懒汉单例模式</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 静态局部变量（线程安全）</span></span><br><span class="line">        <span class="keyword">static</span> MyClass instance;</span><br><span class="line">        <span class="keyword">return</span> &amp;instance;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x55b578666192</span><br><span class="line">address: 0x55b578666192</span><br><span class="line">equals: true</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>C++ 基于 DCL（双重检查锁）实现懒汉单例模式</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 第一次获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">load</span>(std::memory_order_acquire);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第一次检测单例对象是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁</span></span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次获取单例对象</span></span><br><span class="line">            ptr = m_instance.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次检测单例对象是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 初始化单例对象</span></span><br><span class="line">                ptr = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置单例对象</span></span><br><span class="line">                m_instance.<span class="built_in">store</span>(ptr, std::memory_order_release);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 销毁单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">destroyInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 获取互斥锁</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">exchange</span>(<span class="literal">nullptr</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放单例对象</span></span><br><span class="line">        <span class="keyword">if</span> (ptr != <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="keyword">delete</span> ptr;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::mutex m_mutex;                <span class="comment">// 互斥锁（静态变量）</span></span><br><span class="line">    <span class="keyword">static</span> std::atomic&lt;MyClass*&gt; m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（互斥锁）</span></span><br><span class="line">std::mutex MyClass::m_mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">std::atomic&lt;MyClass*&gt; <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放单例对象</span></span><br><span class="line">    MyClass::<span class="built_in">destroyInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">MyClass</span>()</span><br><span class="line">address: <span class="number">0x55ec9d5bbeb0</span></span><br><span class="line">address: <span class="number">0x55ec9d5bbeb0</span></span><br><span class="line">equals: <span class="literal">true</span></span><br><span class="line">~<span class="built_in">MyClass</span>()</span><br></pre></td></tr></tbody></table></figure><hr><p>在写单例模式时，很多开发者喜欢提供一个类似 <code>destroyInstance()</code> 的方法来手动释放内存。开发者可能觉得自己在里面加了互斥锁（Mutex）和原子操作（Atomic）就已经万无一失了，但实际上，这在多线程环境下埋下了一个致命的隐患。</p><ul><li><p>假设有以下并发场景：</p><ul><li>(1) 线程 A 调用 <code>getInstance()</code>，通过了空指针检查，成功拿到了单例对象的指针 <code>ptr</code>。</li><li>(2) 线程 B 此时突然调用了 <code>destroyInstance()</code>。因为有锁保护，它成功将内部指针置空，并无情地执行了 <code>delete ptr;</code>。</li><li>(3) 线程 A 根本不知道单例对象已经被销毁。它兴高采烈地拿着手里的 <code>ptr</code> 去调用成员函数（例如 <code>ptr-&gt;doSomething()</code>）。</li></ul></li><li><p>错误原因分析：</p><ul><li>错误结果：线程 A 访问了已经被释放内存的单例对象指针（野指针 / 悬空指针），引发未定义行为，导致程序直接崩溃。</li><li>核心矛盾：锁和原子操作只能保护单例 “指针本身” 的修改是安全的，但它们无法保护其他线程手中已经持有的、指向单例内存的存根。</li><li>最佳实践：在现代 C++（C++11 及以后）中，永远不要尝试手动销毁单例对象。最优雅、最安全的做法是使用静态局部变量来实现单例模式。</li></ul></li><li><p>正常释放单例对象的内存：</p></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 第一次获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">load</span>(std::memory_order_acquire);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第一次检测单例对象是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁</span></span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次获取单例对象</span></span><br><span class="line">            ptr = m_instance.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次检测单例对象是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 初始化单例对象</span></span><br><span class="line">                ptr = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 静态局部变量（用于自动释放单例对象的内存）</span></span><br><span class="line">                <span class="keyword">static</span> GcClass gc;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置单例对象</span></span><br><span class="line">                m_instance.<span class="built_in">store</span>(ptr, std::memory_order_release);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 内部定义的 GC 类，用于自动释放单例对象的内存</span></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">GcClass</span> {</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="comment">// 构造函数</span></span><br><span class="line">        <span class="built_in">GcClass</span>() {</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 析构函数</span></span><br><span class="line">        ~<span class="built_in">GcClass</span>() {</span><br><span class="line">            MyClass* ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 释放单例对象的内存</span></span><br><span class="line">                <span class="keyword">delete</span> ptr;</span><br><span class="line">                ptr = <span class="literal">nullptr</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::mutex m_mutex;                <span class="comment">// 互斥锁（静态变量）</span></span><br><span class="line">    <span class="keyword">static</span> std::atomic&lt;MyClass*&gt; m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（互斥锁）</span></span><br><span class="line">std::mutex MyClass::m_mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">std::atomic&lt;MyClass*&gt; <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x55c7c52d6eb0</span><br><span class="line">address: 0x55c7c52d6eb0</span><br><span class="line">equals: true</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><h4 id="共享数据问题分析"><a href="#共享数据问题分析" class="headerlink" title="共享数据问题分析"></a>共享数据问题分析</h4><p>在多线程环境中，下面的单例设计模式代码存在严重的线程安全问题（共享数据问题）。这是因为在多线程环境下，下述代码的线程安全问题核心在于 <code>getInstance()</code> 中的 “检查 - 然后 - 操作” 竞态条件：当多个线程同时首次调用 <code>getInstance()</code> 并检测到 <code>m_instance == nullptr</code> 时，它们都会通过 <code>new MyClass()</code> 创建各自独立的实例，导致单例规则被破坏，并且由于析构函数私有，这些多余的对象永远无法被正确释放，造成内存泄漏；同时，<code>new</code> 操作本身涉及内存分配和构造两个步骤，若无内存屏障保护，其他线程可能看到一个未完全构造完成的对象，进而引发未定义行为。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">if</span> (m_instance == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 初始化单例对象</span></span><br><span class="line">            m_instance = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> m_instance;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> MyClass* m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">MyClass* <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass* ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; ptr &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(run)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下（可以发现 MyClass 类的构造函数被调用了多次，破坏了单例规则）：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x7f84e4000b70</span><br><span class="line">MyClass()</span><br><span class="line">address: 0x7f84dc000b70</span><br><span class="line">MyClass()</span><br><span class="line">address: 0x7f84ec000b70</span><br></pre></td></tr></tbody></table></figure><h4 id="共享数据问题解决"><a href="#共享数据问题解决" class="headerlink" title="共享数据问题解决"></a>共享数据问题解决</h4><h5 id="第一种解决方案"><a href="#第一种解决方案" class="headerlink" title="第一种解决方案"></a>第一种解决方案</h5><p>在多线程环境中，可以使用互斥量（<code>mutex</code>）+ 原子操作（<code>atomic</code>） + DCL（双重检查锁）来解决上面单例设计模式代码存在的线程安全问题（共享数据问题）。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 第一次获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">load</span>(std::memory_order_acquire);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第一次检测单例对象是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁</span></span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次获取单例对象</span></span><br><span class="line">            ptr = m_instance.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次检测单例对象是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 初始化单例对象</span></span><br><span class="line">                ptr = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置单例对象</span></span><br><span class="line">                m_instance.<span class="built_in">store</span>(ptr, std::memory_order_release);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::mutex m_mutex;                <span class="comment">// 互斥锁（静态变量）</span></span><br><span class="line">    <span class="keyword">static</span> std::atomic&lt;MyClass*&gt; m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（互斥锁）</span></span><br><span class="line">std::mutex MyClass::m_mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">std::atomic&lt;MyClass*&gt; <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass* ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; ptr &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(run)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下（可以发现 MyClass 类的构造函数不会被调用多次，单例规则没有被破坏）：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x7f44e0000b70</span><br><span class="line">address: 0x7f44e0000b70</span><br><span class="line">address: 0x7f44e0000b70</span><br></pre></td></tr></tbody></table></figure><h5 id="第二种解决方案"><a href="#第二种解决方案" class="headerlink" title="第二种解决方案"></a>第二种解决方案</h5><p>在多线程环境中，还可以使用 <code>std::call_once()</code> 来解决上面单例设计模式代码存在的线程安全问题（共享数据问题）。<code>std::call_once()</code> 是 C++ 11 标准库提供的一个线程同步原语，位于 <code>&lt;mutex&gt;</code> 头文件中，其核心作用是在多线程环境下确保某个可调用对象（如函数、Lambda 表达式或函数对象）仅被执行一次，即使该函数被多个线程同时调用。它依托于一个静态或全局的 <code>std::once_flag</code> 标记实例，通过将待执行的目标函数与这个标记一同传递给 <code>std::call_once()</code>，系统便会以原子操作的方式保证：在所有并发调用中，最终只有第一个抵达的线程会实际执行目标函数，而其余线程则会阻塞等待直到该次执行完成；若执行过程中抛出异常，则标记不会置位，系统会自动选择下一个线程重新尝试执行，从而提供了强健的一次性初始化机制。这一工具尤其适用于延迟初始化（如单例模式创建）、共享资源的首次设置，或任何需要 “只运行一次” 的线程安全场景，相较于使用互斥锁加双重检查锁（DCL）的手写方案，它不仅代码更简洁、意图更清晰，而且能避免内存可见性和指令重排序等底层陷阱，是 C++ 并发编程中实现线程安全一次初始化的推荐惯用法。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建单例对象（私有静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">createInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        m_instance = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass&amp; <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 创建单例对象（永远只执行一次）</span></span><br><span class="line">        std::<span class="built_in">call_once</span>(m_flag, createInstance);</span><br><span class="line">        <span class="keyword">return</span> *m_instance;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::once_flag m_flag;  <span class="comment">// 一次性标记</span></span><br><span class="line">    <span class="keyword">static</span> MyClass* m_instance;    <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（一次性标记）</span></span><br><span class="line">std::once_flag MyClass::m_flag;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">MyClass* <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass&amp; ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; &amp;ptr &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(run)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下（可以发现 MyClass 类的构造函数不会被调用多次，单例规则没有被破坏）：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x7f9eb8000b70</span><br><span class="line">address: 0x7f9eb8000b70</span><br><span class="line">address: 0x7f9eb8000b70</span><br></pre></td></tr></tbody></table></figure>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十</title>
    <link href="https://www.techgrow.cn/posts/badb9ea0.html"/>
    <id>https://www.techgrow.cn/posts/badb9ea0.html</id>
    <published>2025-11-28T13:55:33.000Z</published>
    <updated>2025-11-28T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a>、<a href="/posts/8c35d945.html">C++ 从入门到精通之十二</a></li><li><a href="/posts/ce694f48.html">C++ 从入门到精通之十三</a>、<a href="/posts/82bb2356.html">C++ 从入门到精通之十四</a></li></ul><span id="more"></span><h2 id="C-并发编程"><a href="#C-并发编程" class="headerlink" title="C++ 并发编程"></a>C++ 并发编程</h2><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/a2a7ad9b.html">C++ 多线程编程之一</a></li></ul></div><h3 id="线程开始和结束运行"><a href="#线程开始和结束运行" class="headerlink" title="线程开始和结束运行"></a>线程开始和结束运行</h3><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>进程是否执行完毕，是以主线程是否执行完为标志的。<strong>主线程一旦结束，整个进程就会终止，未执行完的子线程也会被操作系统强制结束</strong>。因此，若希望子线程继续运行，那么就必须保持主线程不结束。</li><li>值得一提的是，只有当子线程被设计为必须完成的核心任务时，主线程提前结束导致子线程被操作系统强杀，这才属于程序缺陷或不合格的程序设计；若子线程设计为<strong>守护线程</strong>或完成辅助性任务，其随主线程终止属于正常行为。</li></ul></div><blockquote><p><strong>thread 与 join () 的使用</strong></p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">        std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    }</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>thread 与 detach () 的使用</strong></p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">        std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    }</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下，可以发现在子线程还未执行完，进程（主线程）就已经正常退出：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">main thread end.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 C++ 中，子线程调用 <code>detach()</code> 后，<code>std::thread</code> 对象与底层线程的关联会被切断，子线程转为后台运行（即变成守护线程），由 C++ 运行时接管。子线程结束运行后，C++ 运行时会自动清理其资源。<strong>特别注意，子线程调用 <code>detach()</code> 后，不能再调用 <code>join()</code>，否则程序会异常退出（崩溃）；子线程是否可以调用 <code>join()</code>，可以调用 <code>joinable()</code> 来判断</strong>。</p></div><h3 id="线程创建的其他方式"><a href="#线程创建的其他方式" class="headerlink" title="线程创建的其他方式"></a>线程创建的其他方式</h3><h4 id="使用函数对象创建线程"><a href="#使用函数对象创建线程" class="headerlink" title="使用函数对象创建线程"></a>使用函数对象创建线程</h4><blockquote><p>使用函数对象（仿函数）创建线程</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp; mc) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 () 操作运算符</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">            std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义函数对象（仿函数）</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="comment">// 注意：这里底层会将 mc 对象复制到子线程里面去，因此即使 mc 对象提前销毁，也不会影响子线程的执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">MyClass(const MyClass &amp;)   // 将主线程中的 MyClass 对象复制进子线程中</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">sub thread end.</span><br><span class="line">~MyClass()            // 这里析构的是复制进子线程的 MyClass 对象</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()            // 这里析构的是主线程中定义的 MyClass 对象 mc</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在上面的代码中，会额外调用一次类对象的拷贝构造函数，导致多拷贝一个类对象。若希望避免多拷贝一份类对象，可以改为使用 <code>std::thread t(std::ref(mc));</code> 来创建子线程。</p></div><blockquote><p>使用函数对象创建线程时，应避免引用局部变量</p></blockquote><p>在下面这段代码中，最核心问题是：<code>MyClass</code> 的构造函数接收 <code>int&amp;</code> 引用，该引用绑定到 <code>main</code> 函数的局部变量 <code>i</code>。当子线程执行 <code>detach()</code> 后，主线程可能先于子线程结束运行，导致局部变量 <code>i</code> 被销毁，而子线程中的 <code>m_i</code> 仍持有该悬空引用，后续在 <code>operator()</code> 中访问 <code>m_i</code> 时将产生未定义行为（通常是程序崩溃、数据错乱或看似正常等）。<strong>特别注意，在使用函数对象（仿函数）创建线程时，应避免引用局部变量，否则可能会导致悬空引用（发生未定义行为）</strong>。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> &amp;i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 () 操作运算符</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">            std::cout &lt;&lt; i &lt;&lt; <span class="string">", "</span> &lt;&lt; m_i &lt;&lt; std::endl;</span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> &amp;m_i;  <span class="comment">// 引用</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> i = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义函数对象（引用了局部变量）</span></span><br><span class="line">    <span class="function">MyClass <span class="title">mc</span><span class="params">(i)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="comment">// 注意：这里底层会将 mc 对象复制到子线程里面去，因此即使 mc 对象提前销毁，也不会影响子线程的执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下，可以发现在子线程还未执行完，进程（主线程）就已经正常退出，并且 <code>m_i</code> 成员变量输出的值可能是错误的：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">MyClass(const MyClass &amp;)    // 将主线程中的 MyClass 对象复制进子线程中</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()    // 这里析构的是主线程中定义的 MyClass 对象 mc</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1, 22088    // 由于在主线程中，MyClass 对象引用了局部变量，因此这里输出的 m_i 成员变量的值可能是错误的；因为主线程先结束运行，局部变量 i 所在的栈内存会跟着提前被释放掉</span><br><span class="line">1, 22088    // 由于在主线程中，MyClass 对象引用了局部变量，因此这里输出的 m_i 成员变量的值可能是错误的；因为主线程先结束运行，局部变量 i 所在的栈内存会跟着提前被释放掉</span><br></pre></td></tr></tbody></table></figure><h4 id="使用-Lambda-创建线程"><a href="#使用-Lambda-创建线程" class="headerlink" title="使用 Lambda 创建线程"></a>使用 Lambda 创建线程</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义 Lambda 表达式</span></span><br><span class="line">    <span class="keyword">auto</span> lambda = [] {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">            std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="comment">// 注意：这里底层会将 mc 对象复制到子线程里面去，因此即使 mc 对象提前销毁，也不会影响子线程的执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(lambda)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h4 id="使用成员函数指针创建线程"><a href="#使用成员函数指针创建线程" class="headerlink" title="使用成员函数指针创建线程"></a>使用成员函数指针创建线程</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 成员函数</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">thread_run</span><span class="params">(MyClass &amp;mc)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 主线程中使用 std::ref() 强制传递引用作为线程函数参数后，这里修改的是主线程中的 m_c 对象</span></span><br><span class="line">        mc.m_i = <span class="number">300</span>;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，使用 std::ref() 强制传递引用作为线程函数参数</span></span><br><span class="line">    <span class="comment">// 第一个参数是成员函数指针，第二个参数是类对象（可以是指针或者引用），第三个参数是真正传递给线程函数的参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(&amp;MyClass::thread_run, &amp;m_c, std::ref(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等价于上面的写法</span></span><br><span class="line">    <span class="comment">// std::thread t(&amp;MyClass::thread_run, std::ref(m_c), std::ref(m_c));</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意：如果使用下面这种写法（合法），又会多拷贝一份 m_c 对象，导致线程函数里修改的不是主线程中的 m_c 对象</span></span><br><span class="line">    <span class="comment">// std::thread t(&amp;MyClass::thread_run, m_c, std::ref(m_c));</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><h3 id="线程传递参数的方式"><a href="#线程传递参数的方式" class="headerlink" title="线程传递参数的方式"></a>线程传递参数的方式</h3><h4 id="传递临时对象作为线程参数"><a href="#传递临时对象作为线程参数" class="headerlink" title="传递临时对象作为线程参数"></a>传递临时对象作为线程参数</h4><h5 id="参数传递实战"><a href="#参数传递实战" class="headerlink" title="参数传递实战"></a>参数传递实战</h5><p>这段代码是安全的（正确的），因为主线程必须等待子线程执行结束，然后再往下执行。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_buf)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">this is a string</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h5 id="要避免的陷阱一"><a href="#要避免的陷阱一" class="headerlink" title="要避免的陷阱一"></a>要避免的陷阱一</h5><p>在下面的这段代码中，存在一个严重的悬垂指针问题，这是 C++ 多线程开发中非常经典的错误。核心问题是：参数 <code>m_buf</code> 传递的是指针，但在子线程执行 <code>detach()</code> 后，主线程可能先结束运行，导致局部变量 <code>m_buf</code> 被销毁，最终子线程会访问已释放的内存。</p><ul><li><p>(1) 代码安全的部分：<code>int &amp;</code> 参数</p><ul><li>虽然函数签名是 <code>const int &amp;i</code>，但实际上 <code>std::thread</code> 构造函数会对参数做<strong>按值拷贝</strong>（除非显式使用 <code>std::ref</code>）</li><li>即使 <code>detach()</code> 后主线程销毁了 <code>m_i</code>，子线程持有的也是独立副本，访问是安全的</li></ul></li><li><p> (2) 代码不安全的部分：<code>char *</code> 参数</p><ul><li><code>m_buf</code> 是局部变量，存储在<strong>栈</strong>上</li><li><code>std::thread</code> 只是传递了数组首地址（指针值被拷贝），并没有拷贝字符串内容</li><li>当主线程执行完 <code>return 0</code> 后，<code>m_buf</code> 的内存会被释放 / 覆盖</li><li>子线程若此时继续访问 <code>buf</code>，将发生<strong>未定义行为</strong>（程序可能会崩溃、输出乱码、看似正常等）</li></ul></li></ul><blockquote><p>问题代码</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_buf)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案</p></blockquote><ul><li>解决方案一：使用 <code>join()</code> 替代 <code>detach()</code>，让主线程等待子线程结束 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_buf)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();   <span class="comment">// 主线程等待子线程结束</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案二：传递字符串副本 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, std::string(m_buf))</span></span>;   <span class="comment">// 使用临时对象</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案三：使用智能指针 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    <span class="keyword">auto</span> sp_buf = std::make_shared&lt;<span class="keyword">char</span>[]&gt;(<span class="built_in">strlen</span>(m_buf) + <span class="number">1</span>);</span><br><span class="line">    <span class="built_in">strcpy</span>(sp_buf.<span class="built_in">get</span>(), m_buf);</span><br><span class="line"></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">([m_i, sp_buf]() { </span></span></span><br><span class="line"><span class="params"><span class="function">        func(m_i, sp_buf.get()); </span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案四：使用值传递而不是引用专递（<strong>最佳实践</strong>）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 最安全的写法（不使用引用或指针，直接传递值）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> i, <span class="keyword">const</span> std::string buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 传递字符串副本（使用临时对象）</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, std::string(m_buf))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 现在执行 detach() 安全了，因为 string 已经被拷贝到线程内部</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">总结</p><ul><li>本质问题：代码将指向局部栈数组的指针传递给分离线程（子线程），主线程退出后子线程访问已销毁的内存，导致未定义行为。</li><li>类似风险：传递局部对象的引用、指向局部对象的指针、迭代器等给调用过 <code>detach()</code> 的子线程（分离线程）。</li><li>最佳实践：除非明确知道对象生命周期大于线程生命周期，否则应避免在调用过 <code>detach()</code> 的子线程中使用指针或者引用，尽量传递值类型或智能指针；<code>join()</code> 比 <code>detach()</code> 更安全，能明确控制生命周期。</li></ul></div><h5 id="要避免的陷阱二"><a href="#要避免的陷阱二" class="headerlink" title="要避免的陷阱二"></a>要避免的陷阱二</h5><p>在下面的这段代码中，存在一个严重的悬空引用问题，这是 C++ 多线程开发中非常经典的错误。核心问题是：由于 <code>m_c</code> 局部变量通过隐式类型转换构造 MyClass 临时对象的时机不确定，因此代码是不安全的。</p><ul><li><p>(1) 隐式类型转换的细节：</p><ul><li><code>func()</code> 期望第二个参数是 <code>const MyClass &amp;</code>，但传递的是 <code>int</code>，这里会发生隐式类型转换，也就是编译器会用 <code>m_c</code>（200）调用 <code>MyClass(int)</code> 构造函数创建一个临时对象。</li></ul></li><li><p>(2) 代码安全的部分：<code>int &amp;</code> 参数</p><ul><li>虽然函数签名是 <code>const int &amp;i</code>，但实际上 <code>std::thread</code> 构造函数会对参数做<strong>按值拷贝</strong>（除非显式使用 <code>std::ref</code>）</li><li>即使 <code>detach()</code> 后主线程销毁了 <code>m_i</code>，子线程持有的也是独立副本，访问是安全的</li></ul></li><li><p> (3) 代码不安全的部分：<code>MyClass &amp;</code> 参数</p><ul><li><code>m_c</code>（200）会通过隐式类型转换构造一个 <code>MyClass</code> 临时对象</li><li>但是 <code>std::thread</code> 并不是立即执行线程函数，它会先拷贝或移动参数到内部存储</li><li>当线程函数真正执行时，<code>MyClass</code> 临时对象的构造时机可能已经晚于主线程中 <code>m_c</code> 的销毁</li><li>因此 <code>const MyClass &amp;mc</code> 可能绑定到一个未正确构造的 <code>MyClass</code> 对象</li><li>这会导致悬空引用，访问 <code>mc</code> 属于未定义行为（通常是程序崩溃、数据错乱或看似正常等）</li></ul></li></ul><blockquote><p>问题代码（使用引用 + 隐式类型转换）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">int</span> m_c = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数（这里的 m_c 会通过隐式类型转换构造一个 MyClass 临时对象）</span></span><br><span class="line">    <span class="comment">// 注意：这里的 MyClass 临时对象是在子线程中构造出来的，而且有可能是在主线程结束运行之后才构造，因此是不安全的</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_c)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案</p></blockquote><ul><li>解决方案一：使用引用 + 手动构造栈对象 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;  <span class="comment">// 栈上的对象（临时对象）</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数</span></span><br><span class="line">    <span class="comment">// 在创建子线程时，会拷贝参数 m_c 到子线程的内部存储中，因此在执行 detach() 后继续访问 m_c 是安全的</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_c)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案二：使用引用 + 手动构造临时对象（<strong>最佳实践</strong>）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">int</span> m_c = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数（构造一个 MyClass 临时对象）</span></span><br><span class="line">    <span class="comment">// 注意：这里的 MyClass 临时对象是在主线程中构造出来的，而且是在主线程结束运行之前构造出来，因此是安全的</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, MyClass(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="参数传递的最佳实践"><a href="#参数传递的最佳实践" class="headerlink" title="参数传递的最佳实践"></a>参数传递的最佳实践</h5><ul><li>建议不要使用 <code>detach()</code>，只使用 <code>join()</code>。这样可以避免因局部变量失效而导致子线程访问非法内存的问题。  </li><li>如果确实要使用 <code>detach()</code>，应遵守以下规则：<ul><li>传递基础类型（如 <code>int</code>）给线程时，建议使用<strong>值传递</strong>，不要使用引用或指针。  </li><li>传递类对象（如 <code>MyClass</code>）给线程时：<ul><li>(1) 应避免隐式类型转换</li><li> (2) 建议在创建线程的同一行代码中直接构建临时对象，并在线程函数的参数列表中使用<strong>引用</strong>来接收该对象，这样做可以避免一次额外的类对象拷贝构造（示例代码如下）<figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        </span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        </span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 基础类型使用值传递，类对象使用引用传递</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">int</span> m_c = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程时，在创建线程的同一行代码中直接构建临时对象</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, MyClass(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置主线程无需等待子线程执行结束才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul></li></ul><h4 id="传递类对象、智能指针作为线程参数"><a href="#传递类对象、智能指针作为线程参数" class="headerlink" title="传递类对象、智能指针作为线程参数"></a>传递类对象、智能指针作为线程参数</h4><p>下面这段代码是安全的（因为使用 <code>join()</code> 而不是 <code>detach()</code>），但线程函数里面修改的不是主线程中的 <code>m_c</code> 对象。因为在子线程创建时，已经将主线程中的 <code>m_c</code> 对象拷贝到子线程的内部存储中，这里的 <code>MyClass &amp;mc</code> 引用绑定的是这份拷贝，所以修改不会影响主线程中的 <code>m_c</code> 对象。<strong>思考问题：如何才能够在子线程中正确修改主线程中的 <code>mc</code> 对象呢？</strong></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">mutable</span> <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 注意：子线程创建时已经将 m_c 对象拷贝到子线程的内部存储中，这里的 mc 引用绑定的是这份拷贝，因此修改不会影响主线程中的 m_c 对象</span></span><br><span class="line">    mc.m_i = <span class="number">300</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，会拷贝 m_c 对象到子线程的内部存储中</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_c)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">MyClass(const MyClass &amp;)</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">~MyClass()</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案一，使用 <code>std::ref()</code> 强制传递引用作为线程函数参数，避免多拷贝一份类对象</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 主线程中使用 std::ref() 强制传递引用后，这里修改的是主线程中的 m_c 对象</span></span><br><span class="line">    mc.m_i = <span class="number">300</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，使用 std::ref() 强制传递引用作为线程函数参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, std::ref(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案二，传递智能指针作为线程函数参数，避免多拷贝一份类对象</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(std::unique_ptr&lt;MyClass&gt; up)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 主线程中传递智能指针后，这里修改的是主线程中的 m_c 对象</span></span><br><span class="line">    up-&gt;m_i = <span class="number">300</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">std::unique_ptr&lt;MyClass&gt; <span class="title">m_c</span><span class="params">(<span class="keyword">new</span> MyClass(<span class="number">200</span>))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，传递智能指针作为线程函数参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, std::move(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">~MyClass()</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h3 id="并发与多线程"><a href="#并发与多线程" class="headerlink" title="并发与多线程"></a>并发与多线程</h3><h4 id="创建和等待多个线程"><a href="#创建和等待多个线程" class="headerlink" title="创建和等待多个线程"></a>创建和等待多个线程</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> num)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">100</span>));</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 10 个线程，同时这 10 个线程会立即执行（多个线程之间的启动顺序是不确定的）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) {</span><br><span class="line">        threads.<span class="built_in">emplace_back</span>(std::<span class="built_in">thread</span>(func, i));</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程等待所有线程执行完毕，然后才继续往下执行</span></span><br><span class="line">    <span class="keyword">for</span> (std::vector&lt;std::thread&gt;::iterator it = threads.<span class="built_in">begin</span>(); it != threads.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">        it-&gt;<span class="built_in">join</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread 0 start.</span><br><span class="line">sub thread 1 start.</span><br><span class="line">sub thread 2 start.</span><br><span class="line">sub thread 3 start.</span><br><span class="line">sub thread 5 start.</span><br><span class="line">sub thread 4 start.</span><br><span class="line">sub thread 6 start.</span><br><span class="line">sub thread 8 start.</span><br><span class="line">sub thread 7 start.</span><br><span class="line">sub thread 9 start.</span><br><span class="line">sub thread 0 end.</span><br><span class="line">sub thread 1 end.</span><br><span class="line">sub thread 2 end.</span><br><span class="line">sub thread 3 end.</span><br><span class="line">sub thread 5 end.</span><br><span class="line">sub thread 4 end.</span><br><span class="line">sub thread 6 end.</span><br><span class="line">sub thread 8 end.</span><br><span class="line">sub thread 7 end.</span><br><span class="line">sub thread 9 end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h4 id="数据共享问题的分析"><a href="#数据共享问题的分析" class="headerlink" title="数据共享问题的分析"></a>数据共享问题的分析</h4><h5 id="只读的数据"><a href="#只读的数据" class="headerlink" title="只读的数据"></a>只读的数据</h5><div class="admonition note"><p class="admonition-title">关键点</p><ul><li>在多线程环境下，只读的共享数据是不存在线程安全问题的，不需要特殊的处理手段，直接读就可以。</li></ul></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 共享数据（只读）</span></span><br><span class="line">std::vector&lt;<span class="keyword">int</span>&gt; g_v = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> num)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" read "</span> &lt;&lt; g_v[<span class="number">0</span>] &lt;&lt; <span class="string">", "</span> &lt;&lt; g_v[<span class="number">1</span>] &lt;&lt; <span class="string">", "</span> &lt;&lt; g_v[<span class="number">2</span>] &lt;&lt; std::endl;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">50</span>));</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 5 个线程，同时这 5 个线程会立即执行（多个线程之间的启动顺序是不确定的）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) {</span><br><span class="line">        threads.<span class="built_in">emplace_back</span>(std::<span class="built_in">thread</span>(func, i));</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待所有线程执行完毕</span></span><br><span class="line">    <span class="keyword">for</span> (std::vector&lt;std::thread&gt;::iterator it = threads.<span class="built_in">begin</span>(); it != threads.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">        it-&gt;<span class="built_in">join</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread 0 start.</span><br><span class="line">sub thread 0 read 1, 2, 3</span><br><span class="line">sub thread 1 start.</span><br><span class="line">sub thread 1 read 1, 2, 3</span><br><span class="line">sub thread 2 start.</span><br><span class="line">sub thread 2 read 1, 2, 3</span><br><span class="line">sub thread 3 start.</span><br><span class="line">sub thread 3 read 1, 2, 3</span><br><span class="line">sub thread 4 start.</span><br><span class="line">sub thread 4 read 1, 2, 3</span><br><span class="line">sub thread 2 end.</span><br><span class="line">sub thread 1 end.</span><br><span class="line">sub thread 0 end.</span><br><span class="line">sub thread 3 end.</span><br><span class="line">sub thread 4 end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h5 id="有读有写"><a href="#有读有写" class="headerlink" title="有读有写"></a>有读有写</h5><div class="admonition note"><p class="admonition-title">关键点</p><ul><li>在多线程环境下，有读有写的共享数据是存在线程安全问题的（比如 3 个线程同时读数据，2 个线程同时写数据），需要特殊的处理手段。</li><li>常见的处理手段：针对共享数据，使用互斥锁（如 <code>std::mutex</code>）或读写锁（<code>std::shared_mutex</code>）来保证读读不互斥、读写和写写互斥。</li></ul></div><ul><li>问题代码的简单分析<ul><li>以下代码展示了在多线程环境下，多个线程同时读写共享数据 <code>std::vector&lt;int&gt; g_v</code> 而没有使用任何同步机制，从而产生线程安全问题（数据竞争）。</li><li>前 2 个线程执行写操作（修改 <code>vector</code> 内容），后 3 个线程执行读操作（读取 <code>vector</code> 内容）。由于读写同时发生，可能会导致未定义行为（比如读取到不一致的值、程序崩溃等）。</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 共享数据（有读有写）</span></span><br><span class="line">std::vector&lt;<span class="keyword">int</span>&gt; g_v = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> num)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 前两个线程：执行写操作</span></span><br><span class="line">    <span class="keyword">if</span> (num &lt; <span class="number">2</span>) {</span><br><span class="line">        <span class="comment">// 写操作：添加一个新元素</span></span><br><span class="line">        g_v.<span class="built_in">push_back</span>(num);</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" write value "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">// 后三个线程：执行读操作</span></span><br><span class="line">    <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// 读操作：读取 vector 的所有元素</span></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" reads: "</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> val : g_v) {</span><br><span class="line">            std::cout &lt;&lt; val &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟一些工作耗时</span></span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">50</span>));</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 5 个线程，其中 2 个线程执行写操作，3 个线程执行读操作</span></span><br><span class="line">    <span class="comment">// 由于读写同时发生，可能会导致未定义行为（比如读取到不一致的值、程序崩溃等）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) {</span><br><span class="line">        threads.<span class="built_in">emplace_back</span>(func, i);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待所有线程执行完毕</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : threads) {</span><br><span class="line">        t.<span class="built_in">join</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>存在的线程安全问题<ul><li> (1) 数据竞争：写线程修改 <code>g_v</code>（例如 <code>push_back</code> 可能会引发 <code>vector</code> 重新分配内存）时，读线程同时遍历 <code>g_v</code>，会导致读线程访问到无效的内存或未更新的状态。</li><li>(2) 不一致的读取：读线程可能在写线程修改过程中读取到部分旧数据、部分新数据，甚至因迭代器失效导致程序崩溃。</li><li>(3) 未定义行为：根据 C++ 标准，并发读写同一非原子对象且无同步机制时，程序存在未定义行为，可能的表现如下：<ul><li>程序可能正常输出混乱的结果</li><li>程序可能抛出异常（如 <code>vector</code> 迭代器失效）</li><li>程序可能直接崩溃（段错误等）</li></ul></li></ul></li></ul><h4 id="共享数据的保护案例"><a href="#共享数据的保护案例" class="headerlink" title="共享数据的保护案例"></a>共享数据的保护案例</h4><div class="admonition note"><p class="admonition-title">案例背景说明</p><p>在网络游戏服务器的设计中，共享数据的保护是一个典型案例：可以创建两个线程，其中一个线程负责收集玩家的命令并将命令数据写入队列，另一个线程则从队列中取出玩家发来的命令，进行解析并执行玩家所需的动作。值得一提的是，在当前业务场景下建议使用生产者消费者模型来实现，并使用 <code>list</code> 容器作为队列。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列（生产者）</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) {</span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 获取互斥锁（作用域结束后会自动释放锁）</span></span><br><span class="line">                <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 将玩家命令写入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 唤醒等待操作队列的其他线程</span></span><br><span class="line">            queueNotEmptyCondition.<span class="built_in">notify_one</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通知消费者退出</span></span><br><span class="line">        {</span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line">            stop = <span class="literal">true</span>;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 唤醒所有等待线程</span></span><br><span class="line">        queueNotEmptyCondition.<span class="built_in">notify_all</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令（消费者）</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁（作用域结束后会自动释放锁）</span></span><br><span class="line">            <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 当前线程等待队列不为空或者收到退出信号</span></span><br><span class="line">            queueNotEmptyCondition.<span class="built_in">wait</span>(lock, [<span class="keyword">this</span>]() { <span class="keyword">return</span> stop || !msgRecvQueue.<span class="built_in">empty</span>(); });</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 收到退出信号且队列为空</span></span><br><span class="line">            <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 取出队列元素</span></span><br><span class="line">            <span class="keyword">int</span> command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 移除队列元素</span></span><br><span class="line">            msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 尽早释放锁，提高并发性能</span></span><br><span class="line">            lock.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解析并执行玩家命令</span></span><br><span class="line">            <span class="built_in">handleCommand</span>(command);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 模拟处理玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">handleCommand</span><span class="params">(<span class="keyword">int</span> command)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;                     <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;                    <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::condition_variable queueNotEmptyCondition;  <span class="comment">// 消息队列不为空的条件</span></span><br><span class="line">    <span class="keyword">bool</span> stop = <span class="literal">false</span>;                               <span class="comment">// 线程退出标志</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程（生产者）</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程（消费者）</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>list 容器和 vector 容器的适用场景</strong></p></blockquote><table><thead><tr><th>容器</th><th>适用场景（效率高）</th><th>不适用场景（效率低）</th></tr></thead><tbody><tr><td><code>list</code></td><td>- 任意位置插入 / 删除（给定迭代器）<br>- 从头部 / 尾部操作<br>- 合并两个列表</td><td> - 随机访问（不支持 <code>[]</code>）<br>- 查找元素<br>- 遍历（缓存不友好）</td></tr><tr><td><code>vector</code></td><td>- 尾部插入 / 删除<br>- 随机访问（下标访问）<br>- 遍历（缓存友好）</td><td>- 头部 / 中间插入 / 删除<br>- 插入导致重新分配内存</td></tr></tbody></table><div class="admonition note"><p class="admonition-title">提示</p><ul><li>C++ 的 <code>list</code> 容器在任意位置频繁插入和删除数据时效率高（尤其是中间位置），因为它只需调整前后节点的指针，不需要移动其他元素。</li><li>C++ 的 <code>vector</code> 容器在尾部频繁插入和删除数据时效率高（平均时间复杂度 <code>O(1)</code>），但在头部或中间插入 / 删除数据时效率低，因为需要移动后续所有元素。</li></ul></div>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
</feed>
