<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>听寒</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.moew.xyz/</id>
  <link href="https://blog.moew.xyz/" rel="alternate"/>
  <link href="https://blog.moew.xyz/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 听寒</rights>
  <subtitle>Let's debug the world together.</subtitle>
  <title>听寒's Blog</title>
  <updated>2026-06-25T01:53:52.150Z</updated>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="学习" scheme="https://blog.moew.xyz/categories/%E5%AD%A6%E4%B9%A0/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="核心思想"><a href="#核心思想" class="headerlink" title="核心思想"></a>核心思想</h2><p>线段树（Segment Tree）是一种平衡树型数据结构，主要用于区间查询和区间更新问题。</p><p>适用场景：求数组某个区间的和、最大值、最小值。<br>动态更新数组元素，同时仍能快速查询区间信息。</p><p>特点：<br>查询和更新的时间复杂度为 O(log n)。<br>空间复杂度为 O(4n)（常用数组实现，也可以用树形结构存储）。</p><hr><p>求和（动态开点，单点更新）</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SumSegTree</span> &#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        <span class="type">size_t</span> L, R,MID;</span><br><span class="line">        T val;</span><br><span class="line">        Node *left,*right;</span><br><span class="line">        <span class="function">T <span class="title">sum</span><span class="params">(<span class="type">size_t</span> l, <span class="type">size_t</span> r)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span>(l&gt;r) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span>(r &lt; <span class="keyword">this</span>-&gt;L || l &gt; <span class="keyword">this</span>-&gt;R) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span>(l &lt;= <span class="keyword">this</span>-&gt;L &amp;&amp; r &gt;= <span class="keyword">this</span>-&gt;R) <span class="keyword">return</span> <span class="keyword">this</span>-&gt;val;</span><br><span class="line">            T leftvalue=<span class="number">0</span>, rightvalue=<span class="number">0</span>;</span><br><span class="line">            <span class="keyword">if</span>(l&lt;=<span class="keyword">this</span>-&gt;MID &amp;&amp; <span class="keyword">this</span>-&gt;left) &#123;</span><br><span class="line">                leftvalue = <span class="keyword">this</span>-&gt;left-&gt;<span class="built_in">sum</span>(l, r);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(r&gt;<span class="keyword">this</span>-&gt;MID &amp;&amp; <span class="keyword">this</span>-&gt;right) &#123;</span><br><span class="line">                rightvalue = <span class="keyword">this</span>-&gt;right-&gt;<span class="built_in">sum</span>(l, r);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> leftvalue+rightvalue;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="type">void</span> <span class="title">update</span><span class="params">(<span class="type">size_t</span> x, T val)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span>(<span class="keyword">this</span>-&gt;R &lt; x || <span class="keyword">this</span>-&gt;L &gt; x) <span class="keyword">return</span>;</span><br><span class="line">            <span class="keyword">if</span>(L==R) &#123;</span><br><span class="line">                <span class="comment">//cout &lt;&lt; &quot;[update] set leaf &quot; &lt;&lt; L &lt;&lt; &quot; &quot; &lt;&lt; x &lt;&lt; endl;</span></span><br><span class="line">                <span class="keyword">this</span>-&gt;val = val;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(!<span class="keyword">this</span>-&gt;left) <span class="built_in">addNode</span>(<span class="keyword">this</span>-&gt;left, <span class="keyword">this</span>-&gt;L, <span class="keyword">this</span>-&gt;MID);</span><br><span class="line">            <span class="keyword">if</span>(!<span class="keyword">this</span>-&gt;right) <span class="built_in">addNode</span>(<span class="keyword">this</span>-&gt;right, <span class="keyword">this</span>-&gt;MID<span class="number">+1</span>, <span class="keyword">this</span>-&gt;R);</span><br><span class="line">            <span class="keyword">if</span>(x&lt;=<span class="keyword">this</span>-&gt;MID) &#123;</span><br><span class="line">                <span class="keyword">this</span>-&gt;left-&gt;<span class="built_in">update</span>(x, val);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(x&gt;<span class="keyword">this</span>-&gt;MID) &#123;</span><br><span class="line">                <span class="keyword">this</span>-&gt;right-&gt;<span class="built_in">update</span>(x, val);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">this</span>-&gt;val = <span class="keyword">this</span>-&gt;left-&gt;val + <span class="keyword">this</span>-&gt;right-&gt;val; <span class="comment">// 此外，这里要注意我们维持的是树本身的性质（即使x只涉及左孩子或右孩子，我们也要记得加上另一个节点才能维持树的性质）</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="type">void</span> <span class="title">addNode</span><span class="params">(Node*&amp;node, <span class="type">size_t</span> tl, <span class="type">size_t</span> tr)</span> </span>&#123;</span><br><span class="line">            node = <span class="keyword">new</span> <span class="built_in">Node</span>();</span><br><span class="line">            node-&gt;L = tl;</span><br><span class="line">            node-&gt;R = tr;</span><br><span class="line">            node-&gt;val = <span class="number">0</span>;</span><br><span class="line">            node-&gt;MID = tl+(tr-tl)/<span class="number">2</span>;</span><br><span class="line">            node-&gt;left=node-&gt;right=<span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    Node root;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">SumSegTree</span>(<span class="type">size_t</span> MAXN) &#123;</span><br><span class="line">        root.L = <span class="number">1</span>;</span><br><span class="line">        root.R = MAXN;</span><br><span class="line">        root.MID = <span class="number">1</span>+(MAXN<span class="number">-1</span>)/<span class="number">2</span>;</span><br><span class="line">        root.left=root.right=<span class="literal">nullptr</span>;</span><br><span class="line">        root.val = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">update</span><span class="params">(<span class="type">size_t</span> x, T val)</span> </span>&#123;</span><br><span class="line">        root.<span class="built_in">update</span>(x, val);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">T <span class="title">sum</span><span class="params">(<span class="type">size_t</span> l, <span class="type">size_t</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> root.<span class="built_in">sum</span>(l, r);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>例题：LEETCODE307：<a href="https://leetcode.cn/problems/range-sum-query-mutable/">https://leetcode.cn/problems/falling-squares/</a></p><figure class="highlight c++"><table><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="keyword">class</span> <span class="title class_">NumArray</span> &#123;</span><br><span class="line">    SumSegTree&lt;<span class="type">int</span>&gt; t;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">NumArray</span>(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span><br><span class="line">    : <span class="built_in">t</span>(nums.<span class="built_in">size</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;nums.<span class="built_in">size</span>(); i++) &#123;</span><br><span class="line">            t.<span class="built_in">update</span>(i<span class="number">+1</span>, nums[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">update</span><span class="params">(<span class="type">int</span> index, <span class="type">int</span> val)</span> </span>&#123;</span><br><span class="line">        t.<span class="built_in">update</span>(index<span class="number">+1</span>, val);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">sumRange</span><span class="params">(<span class="type">int</span> left, <span class="type">int</span> right)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> t.<span class="built_in">sum</span>(left<span class="number">+1</span>, right<span class="number">+1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Your NumArray object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * NumArray* obj = new NumArray(nums);</span></span><br><span class="line"><span class="comment"> * obj-&gt;update(index,val);</span></span><br><span class="line"><span class="comment"> * int param_2 = obj-&gt;sumRange(left,right);</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><hr><p>最大值（动态开点，懒标记区间修改）</p><figure class="highlight c++"><table><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="keyword">template</span>&lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MaxValueSegTree</span> &#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">const</span> T MIN = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        <span class="type">size_t</span> L, R, MID;</span><br><span class="line">        T val;</span><br><span class="line">        T lazy;</span><br><span class="line">        Node *left, *right;</span><br><span class="line">        <span class="function">T <span class="title">maxValue</span><span class="params">(<span class="type">size_t</span> l, <span class="type">size_t</span> r)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (l &gt; r) <span class="keyword">return</span> MIN;</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">this</span>-&gt;L &gt;= l &amp;&amp; <span class="keyword">this</span>-&gt;R &lt;= r) &#123;</span><br><span class="line">                <span class="comment">// 对于所查询区间，整颗树的信息已经在当前节点包含，无需再递归</span></span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">this</span>-&gt;val;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (r &lt; <span class="keyword">this</span>-&gt;L || l &gt; <span class="keyword">this</span>-&gt;R) &#123;</span><br><span class="line">                <span class="keyword">return</span> MIN;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">pushdown</span>(); <span class="comment">// 不要忘记查询和修改在向下递归前都要pushdown</span></span><br><span class="line">            T leftValue = MIN, rightValue = MIN;</span><br><span class="line">            <span class="keyword">if</span> (l &lt;= <span class="keyword">this</span>-&gt;MID &amp;&amp; <span class="keyword">this</span>-&gt;left) &#123;</span><br><span class="line">                leftValue = <span class="keyword">this</span>-&gt;left-&gt;<span class="built_in">maxValue</span>(l, r);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (r &gt; <span class="keyword">this</span>-&gt;MID &amp;&amp; <span class="keyword">this</span>-&gt;right) &#123;</span><br><span class="line">                rightValue = <span class="keyword">this</span>-&gt;right-&gt;<span class="built_in">maxValue</span>(l, r);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">max</span>(leftValue, rightValue); <span class="comment">// 如要更改为求和等，则这里从子节点汇总也要对应修改</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="type">void</span> <span class="title">setMax</span><span class="params">(<span class="type">size_t</span> l, <span class="type">size_t</span> r, T val)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (l &gt; r) <span class="keyword">return</span>;</span><br><span class="line">            <span class="keyword">if</span> (r &lt; <span class="keyword">this</span>-&gt;L || l &gt; <span class="keyword">this</span>-&gt;R) <span class="keyword">return</span>;</span><br><span class="line">            <span class="keyword">if</span> (l &lt;= <span class="keyword">this</span>-&gt;L &amp;&amp; r &gt;=<span class="keyword">this</span>-&gt;R) &#123;</span><br><span class="line">                <span class="keyword">this</span>-&gt;val = val;</span><br><span class="line">                <span class="keyword">this</span>-&gt;lazy = val;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">pushdown</span>(); <span class="comment">// 不要忘记查询和修改在向下递归前都要pushdown</span></span><br><span class="line">            <span class="keyword">if</span> (l &lt;= <span class="keyword">this</span>-&gt;MID) &#123;</span><br><span class="line">                <span class="keyword">this</span>-&gt;left-&gt;<span class="built_in">setMax</span>(l, r, val);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (r &gt; <span class="keyword">this</span>-&gt;MID) &#123;</span><br><span class="line">                <span class="keyword">this</span>-&gt;right-&gt;<span class="built_in">setMax</span>(l, r, val);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如要更改为求和等，则这里从子节点汇总也要对应修改</span></span><br><span class="line">            <span class="keyword">this</span>-&gt;val = <span class="built_in">max</span>(<span class="keyword">this</span>-&gt;val, <span class="keyword">this</span>-&gt;left-&gt;val); <span class="comment">// 此外，这里要注意我们维持的是树本身的性质（即使子节点所代表的某些点不在修改范围中，我们也要把左右子节点所代表的所有数据都汇总上来，我们）</span></span><br><span class="line">            <span class="keyword">this</span>-&gt;val = <span class="built_in">max</span>(<span class="keyword">this</span>-&gt;val, <span class="keyword">this</span>-&gt;right-&gt;val);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="type">void</span> <span class="title">addNewNode</span><span class="params">(Node *&amp;node, <span class="type">int</span> newTL, <span class="type">int</span> newTR)</span> </span>&#123;</span><br><span class="line">            node = <span class="keyword">new</span> <span class="built_in">Node</span>();</span><br><span class="line">            node-&gt;L = newTL;</span><br><span class="line">            node-&gt;R = newTR;</span><br><span class="line">            node-&gt;MID = newTL + (newTR-newTL)/<span class="number">2</span>;</span><br><span class="line">            node-&gt;val = MIN;</span><br><span class="line">            node-&gt;lazy = MIN;</span><br><span class="line">            node-&gt;left = node-&gt;right = <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="type">void</span> <span class="title">pushdown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span>(!<span class="keyword">this</span>-&gt;left) <span class="built_in">addNewNode</span>(<span class="keyword">this</span>-&gt;left, <span class="keyword">this</span>-&gt;L, <span class="keyword">this</span>-&gt;MID);</span><br><span class="line">            <span class="keyword">if</span>(!<span class="keyword">this</span>-&gt;right) <span class="built_in">addNewNode</span>(<span class="keyword">this</span>-&gt;right, <span class="keyword">this</span>-&gt;MID<span class="number">+1</span>, <span class="keyword">this</span>-&gt;R);</span><br><span class="line">            <span class="keyword">if</span> (<span class="keyword">this</span>-&gt;lazy != MIN) &#123;</span><br><span class="line">                Node* left = <span class="keyword">this</span>-&gt;left;</span><br><span class="line">                Node* right = <span class="keyword">this</span>-&gt;right;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 如要更改为求和等，则这里从子节点汇总也要对应修改，如</span></span><br><span class="line">                <span class="comment">// left-&gt;val += this-&gt;lazy;</span></span><br><span class="line">                <span class="comment">// right-&gt;val += this-&gt;lazy;</span></span><br><span class="line">                <span class="comment">// left-&gt;lazy += this-&gt;lazy;</span></span><br><span class="line">                <span class="comment">// right-&gt;lazy += this-&gt;lazy;</span></span><br><span class="line"></span><br><span class="line">                left-&gt;val = <span class="built_in">max</span>(left-&gt;val, <span class="keyword">this</span>-&gt;lazy);</span><br><span class="line">                right-&gt;val = <span class="built_in">max</span>(right-&gt;val, <span class="keyword">this</span>-&gt;lazy);</span><br><span class="line">                left-&gt;lazy = <span class="built_in">max</span>(left-&gt;lazy, <span class="keyword">this</span>-&gt;lazy);</span><br><span class="line">                right-&gt;lazy = <span class="built_in">max</span>(right-&gt;lazy, <span class="keyword">this</span>-&gt;lazy);</span><br><span class="line">                <span class="keyword">this</span>-&gt;lazy = MIN;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    Node root;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MaxValueSegTree</span>(<span class="type">size_t</span> MAXN) &#123;</span><br><span class="line">        root.L = <span class="number">1</span>;</span><br><span class="line">        root.R = MAXN;</span><br><span class="line">        root.MID = <span class="number">1</span> + (MAXN<span class="number">-1</span>)/<span class="number">2</span>;</span><br><span class="line">        root.val = MIN;</span><br><span class="line">        root.lazy = MIN;</span><br><span class="line">        root.left = root.right = <span class="literal">nullptr</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setMax</span><span class="params">(<span class="type">size_t</span> l, <span class="type">size_t</span> r, T val)</span> </span>&#123;</span><br><span class="line">        root.<span class="built_in">setMax</span>(l, r, val);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">T <span class="title">maxValue</span><span class="params">(<span class="type">size_t</span> l, <span class="type">size_t</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> root.<span class="built_in">maxValue</span>(l, r);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>例题：LEETCODE699：<a href="https://leetcode.cn/problems/falling-squares/">https://leetcode.cn/problems/falling-squares/</a></p><figure class="highlight c++"><table><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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">fallingSquares</span><span class="params">(vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&amp; positions)</span> </span>&#123;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">        ans.<span class="built_in">reserve</span>(positions.<span class="built_in">size</span>());</span><br><span class="line">        <span class="type">int</span> maxh = <span class="number">0</span>;</span><br><span class="line">        <span class="function">MaxValueSegTree&lt;<span class="type">int</span>&gt; <span class="title">t</span><span class="params">(<span class="number">100000000</span>)</span></span>;</span><br><span class="line">        <span class="keyword">for</span> (vector&lt;<span class="type">int</span>&gt;&amp; range: positions) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">size_t</span> l = range[<span class="number">0</span>], r = range[<span class="number">0</span>] + range[<span class="number">1</span>]<span class="number">-1</span>;</span><br><span class="line">            <span class="type">int</span> h = t.<span class="built_in">maxValue</span>(l, r);</span><br><span class="line">            t.<span class="built_in">setMax</span>(l, r, h+range[<span class="number">1</span>]);</span><br><span class="line">            maxh = <span class="built_in">max</span>(maxh, h+range[<span class="number">1</span>]);</span><br><span class="line">            ans.<span class="built_in">push_back</span>(maxh);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.moew.xyz/posts/b98efb78.html</id>
    <link href="https://blog.moew.xyz/posts/b98efb78.html"/>
    <published>2026-03-10T19:07:26.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>算法学习：线段树</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="学习" scheme="https://blog.moew.xyz/categories/%E5%AD%A6%E4%B9%A0/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="核心思想"><a href="#核心思想" class="headerlink" title="核心思想"></a>核心思想</h2><p>树状数组本质上是：<br>用数组模拟一棵隐式的二叉树，每个节点维护一段区间的和。</p><p>普通前缀和：a1 a2 a3 a4 a5 a6 a7 a8</p><p>前缀和数组：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">s1 = a1</span><br><span class="line">s2 = a1+a2</span><br><span class="line">s3 = a1+a2+a3</span><br></pre></td></tr></table></figure><p>问题：如果 a3 改变，需要 更新很多位置 → O(n)</p><p>树状数组解决方式：把区间拆成不同长度的块进行存储。</p><table><thead><tr><th>位置</th><th>维护区间</th></tr></thead><tbody><tr><td>1</td><td>[1,1]</td></tr><tr><td>2</td><td>[1,2]</td></tr><tr><td>3</td><td>[3,3]</td></tr><tr><td>4</td><td>[1,4]</td></tr><tr><td>5</td><td>[5,5]</td></tr><tr><td>6</td><td>[5,6]</td></tr><tr><td>7</td><td>[7,7]</td></tr><tr><td>8</td><td>[1,8]</td></tr></tbody></table><p>每个节点维护一个 长度为 lowbit(i) 的区间[i - lowbit(i) + 1 , i]。</p><p>例如：i &#x3D; 6, lowbit(6) &#x3D; 2<br>区间:[6-2+1 , 6]&#x3D;[5 , 6]</p><h2 id="关键函数：lowbit"><a href="#关键函数：lowbit" class="headerlink" title="关键函数：lowbit"></a>关键函数：lowbit</h2><p>树状数组最核心的一行代码：<br><code>lowbit(x) = x &amp; (-x)</code><br>作用：取二进制中最低位的1</p><p>例子：</p><table><thead><tr><th>x</th><th>二进制</th><th>lowbit</th></tr></thead><tbody><tr><td>6</td><td>110</td><td>2</td></tr><tr><td>8</td><td>1000</td><td>8</td></tr><tr><td>12</td><td>1100</td><td>4</td></tr></tbody></table><h2 id="前缀和查询"><a href="#前缀和查询" class="headerlink" title="前缀和查询"></a>前缀和查询</h2><p>查询：</p><p>sum(1..x)</p><p>方法：</p><p>不断 跳父节点</p><figure class="highlight c++"><table><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 class="keyword">while</span>(x &gt; <span class="number">0</span>)&#123;</span><br><span class="line">    sum += tree[x]</span><br><span class="line">    x -= <span class="built_in">lowbit</span>(x)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>示例：<br>求 sum(7)<br>7 -&gt; 6 -&gt; 4 -&gt; 0</p><p>累加区间：<br>[7,7]<br>[5,6]<br>[1,4]</p><p>合起来就是：[1,7]</p><p>时间复杂度：O(log n)</p><h2 id="单点修改"><a href="#单点修改" class="headerlink" title="单点修改"></a>单点修改</h2><p>如果：a[x] +&#x3D; v</p><p>需要更新所有 包含x的区间</p><p>代码：</p><figure class="highlight c++"><table><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 class="keyword">while</span>(x &lt;= n)&#123;</span><br><span class="line">    tree[x] += v</span><br><span class="line">    x += <span class="built_in">lowbit</span>(x)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>例如更新：</p><p>x &#x3D; 3</p><p>更新路径：3 -&gt; 4 -&gt; 8 -&gt; 16 …</p><p>因为这些节点维护的区间都包含 3</p><h2 id="完整代码"><a href="#完整代码" class="headerlink" title="完整代码"></a>完整代码</h2><figure class="highlight c++"><table><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"><span class="type">int</span> tree[N];</span><br><span class="line"><span class="type">int</span> n;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">lowbit</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x &amp; -x;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> v)</span></span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(x &lt;= n)&#123;</span><br><span class="line">        tree[x] += v;</span><br><span class="line">        x += <span class="built_in">lowbit</span>(x);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sum</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(x &gt; <span class="number">0</span>)&#123;</span><br><span class="line">        res += tree[x];</span><br><span class="line">        x -= <span class="built_in">lowbit</span>(x);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>区间修改</p><figure class="highlight plaintext"><table><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">void range_add(int l,int r,int v)&#123;</span><br><span class="line">    add(l, v);</span><br><span class="line">    add(r+1, -v);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>单点查询</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">int query(int x)&#123;</span><br><span class="line">    return sum(x);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><p>初始：<br><code>a: 0 0 0 0 0</code><br>操作：<br><code>[2,4] + 3</code></p><p>差分修改：</p><figure class="highlight plaintext"><table><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">d[2] += 3</span><br><span class="line">d[5] -= 3</span><br></pre></td></tr></table></figure><p>查询：<br><code>a[3] = sum(3)</code></p><p>计算：<br><code>d: 0 3 0 0 -3</code></p><p>前缀和：<br><code>a: 0 3 3 3 0</code></p><h2 id="复杂度"><a href="#复杂度" class="headerlink" title="复杂度"></a>复杂度</h2><table><thead><tr><th>操作</th><th>复杂度</th></tr></thead><tbody><tr><td>区间修改</td><td>O(log n)</td></tr><tr><td>单点查询</td><td>O(log n)</td></tr></tbody></table><hr><h2 id="为啥查-lowbit，改是-Lowbit"><a href="#为啥查-lowbit，改是-Lowbit" class="headerlink" title="为啥查-lowbit，改是+Lowbit"></a>为啥查-lowbit，改是+Lowbit</h2><p>为什么查询要 x -&#x3D; lowbit(x)：每次减 lowbit，就是跳到“前一个区间”<br>为什么修改要 x +&#x3D; lowbit(x)：所有包含3的区间都要更新</p><p>一句话记忆<br>查：往左跳区间   x -&#x3D; lowbit(x)<br>改：往右跳父节点 x +&#x3D; lowbit(x)</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/b52afee1.html</id>
    <link href="https://blog.moew.xyz/posts/b52afee1.html"/>
    <published>2026-03-10T18:57:26.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>算法学习：树状数组</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="学习" scheme="https://blog.moew.xyz/categories/%E5%AD%A6%E4%B9%A0/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>思想：先到先得，能让则让</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;cmath&gt;</span></span></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">vector&lt;<span class="type">int</span>&gt; evens, odds;</span><br><span class="line"></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; odd2even;</span><br><span class="line">vector&lt;<span class="type">bool</span>&gt; oddmatched;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 此函数可任意改写，或通过临接矩阵记录二分图的边再在此查询</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">can_match</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> </span>&#123;</span><br><span class="line">    x += y;</span><br><span class="line">    <span class="keyword">if</span>(x==<span class="number">1</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="type">int</span> bound = <span class="built_in">sqrt</span>(x);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">2</span>; i&lt;=bound; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span>(x % i == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">match</span><span class="params">(<span class="type">int</span> evenIdx)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> oddIdx=<span class="number">0</span>; oddIdx&lt;odds.<span class="built_in">size</span>(); oddIdx++) &#123;</span><br><span class="line">        <span class="keyword">if</span>(oddmatched[oddIdx]) <span class="keyword">continue</span>;</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">can_match</span>(evens[evenIdx], odds[oddIdx])) &#123;</span><br><span class="line">            oddmatched[oddIdx] = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">if</span>(odd2even[oddIdx]==<span class="number">-1</span> || <span class="built_in">match</span>(odd2even[oddIdx])) &#123;</span><br><span class="line">                odd2even[oddIdx] = evenIdx;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n; cin&gt;&gt;n;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) &#123;</span><br><span class="line">        <span class="type">int</span> x; cin&gt;&gt;x;</span><br><span class="line">        <span class="keyword">if</span>(x%<span class="number">2</span>==<span class="number">0</span>) &#123;</span><br><span class="line">            evens.<span class="built_in">push_back</span>(x);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            odds.<span class="built_in">push_back</span>(x);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    oddmatched.<span class="built_in">resize</span>(odds.<span class="built_in">size</span>(), <span class="literal">false</span>);</span><br><span class="line">    odd2even.<span class="built_in">resize</span>(odds.<span class="built_in">size</span>(), <span class="number">-1</span>); </span><br><span class="line">    <span class="type">int</span> ans=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;evens.<span class="built_in">size</span>(); i++) &#123;</span><br><span class="line">        <span class="built_in">fill</span>(oddmatched.<span class="built_in">begin</span>(), oddmatched.<span class="built_in">end</span>(), <span class="literal">false</span>); <span class="comment">// 注意：已匹配标志仅供当次使用，每次发起下一个元素的匹配都要重置，考虑为什么？</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">match</span>(i)) &#123;</span><br><span class="line">            ans++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; ans;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 64 位输出请用 printf(&quot;%lld&quot;)</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.moew.xyz/posts/eac38302.html</id>
    <link href="https://blog.moew.xyz/posts/eac38302.html"/>
    <published>2026-02-03T18:48:26.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>算法学习：二分图最大匹配-匈牙利算法</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="学习" scheme="https://blog.moew.xyz/categories/%E5%AD%A6%E4%B9%A0/"/>
    <category term="kafka" scheme="https://blog.moew.xyz/tags/kafka/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="Kafka-架构和机制"><a href="#Kafka-架构和机制" class="headerlink" title="Kafka 架构和机制"></a>Kafka 架构和机制</h1><p>Zookeeper: 存储元数据<br>Broker互为主备，Topic按分区存储，Replicas分布在不同节点</p><h2 id="节点角色"><a href="#节点角色" class="headerlink" title="节点角色"></a>节点角色</h2><ul><li>Controller：负责Partition管理和Replicas管理，也执行重分配Partition之类的管理任务。若故障，则从其他Broker中重新选举。Broker节点状态管理：新增&#x2F;下线节点、元数据更新。Topic分区管理：新增&#x2F;删除topic、分区扩容&#x2F;迁移&#x2F;leader切换。-</li><li>Leader：负责某Topic某Partition的读写请求。若故障，则从Follower中重新选举。</li><li>Follower：同步Leader数据。</li><li>Coordinator：负责管理Consumer group，维护消费队列进度。</li></ul><h2 id="zookeeper节点"><a href="#zookeeper节点" class="headerlink" title="zookeeper节点"></a>zookeeper节点</h2><ul><li>&#x2F;controller：中央控制器brokerid等信息</li><li>&#x2F;brokers&#x2F;ids&#x2F;{brokerid, 1-n}：临时znode，broker唯一编号</li><li>&#x2F;topics&#x2F;{topic-name}&#x2F;partitions&#x2F;{partitionid, 0-n}&#x2F;state 持久znode，存储分区leader的brokerId等</li><li>&#x2F;isr_change_notifiction：ISR变更事件通知</li></ul><h2 id="topic创建机制"><a href="#topic创建机制" class="headerlink" title="topic创建机制"></a>topic创建机制</h2><ol><li>执行创建topic动作</li><li>在&#x2F;config&#x2F;topics上注册topic的配置目录</li><li>在&#x2F;brokers&#x2F;topic上注册topic的元数据目录</li><li>监听到新topic写入</li><li>Controller从zk读取topic分区信息</li><li>发送分区副本信息到broker</li><li>broker读取topic配置并在本地创建分区副本</li></ol><h2 id="topic删除机制"><a href="#topic删除机制" class="headerlink" title="topic删除机制"></a>topic删除机制</h2><ol><li>&#x2F;admin&#x2F;delete_topics注册监听</li><li>开始删除topic线程</li><li>阻塞并等待删除事件</li><li>恢复删除线程</li><li>添加&#x2F;admin&#x2F;delete_topics&#x2F;{topic_name}</li><li>删除所有分区、删除zk相关目录、清理contoller相关cache</li></ol><h2 id="leader选举流程"><a href="#leader选举流程" class="headerlink" title="leader选举流程"></a>leader选举流程</h2><h3 id="创建topic时选举"><a href="#创建topic时选举" class="headerlink" title="创建topic时选举"></a>创建topic时选举</h3><ol><li>写入分区信息 &#x2F;brokers&#x2F;topics&#x2F;{topic_name}</li><li>Controller监听到&#x2F;brokers&#x2F;topics变化</li><li>读取分区副本列表，首个副本选举成为leader</li><li>发送LeaderAndIsr请求到broker节点</li></ol><h3 id="Leader失效时选举"><a href="#Leader失效时选举" class="headerlink" title="Leader失效时选举"></a>Leader失效时选举</h3><ol><li>Controller监听&#x2F;brokers&#x2F;ids变化</li><li>对leader在该broker上的所有partition重新选举</li><li>获取分区isr，选举首个可用节点作为分区新leader</li><li>发送LeaderAndIsr请求到broker节点</li></ol><h2 id="分区扩容流程"><a href="#分区扩容流程" class="headerlink" title="分区扩容流程"></a>分区扩容流程</h2><ol><li>修改&#x2F;brokers&#x2F;topics&#x2F;{topic_name}</li><li>Controller监听到&#x2F;brokers&#x2F;topics变化</li><li>读取分区信息，发送分区创建请求到broker</li><li>broker读取topic配置并在本地创建分区副本</li></ol><p>重分区和重新分配：可能需要进行重分区和重新分配。这涉及到数据的重新分布和重新平衡，可能会导致一段时间内的性能下降和延迟增加。</p><h2 id="副本迁移流程"><a href="#副本迁移流程" class="headerlink" title="副本迁移流程"></a>副本迁移流程</h2><p>先扩容新副本再下线旧副本</p><ol><li>写入&#x2F;admin&#x2F;reassign_partition</li><li>Controller监听到副本迁移</li><li>Controller注册&#x2F;brokers&#x2F;topics&#x2F;{topic_name}&#x2F;partitions&#x2F;{partitionid}&#x2F;state</li><li>发送请求到新副本所在broker</li><li>broker读取topic配置并创建本地副本</li><li>新副本从原有leader同步数据</li><li>新副本加入ISR，修改zk上分区状态</li><li>选举新leader，停止老副本，使用新副本<br>分区副本迁移不会中断该分区的生产、消费请求</li></ol><p>若Topic为单副本，扩容期间无法对该Topic生产消息或消费消息，会造成业务中断。</p><h1 id="kafka-utils"><a href="#kafka-utils" class="headerlink" title="kafka utils"></a>kafka utils</h1><p>可–help</p><h2 id="kafka-topics-sh"><a href="#kafka-topics-sh" class="headerlink" title="kafka-topics.sh"></a>kafka-topics.sh</h2><ul><li>create</li><li>delete</li><li>list</li><li>describe</li><li>topic</li><li>partition</li><li>replication-factor</li><li>config</li></ul><p>示例：</p><figure class="highlight plaintext"><table><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">kafka-topics.sh --bootstrap-server xx:9091 --list</span><br><span class="line">kafka-topics.sh --bootstrap-server xx:9091 --topic toppicName --describe</span><br><span class="line">kafka-topics.sh --bootstrap-server xx:9091 --topic toppicName --create --parition 3 --replication-factor 2</span><br><span class="line">kafka-topics.sh --bootstrap-server xx:9091 --topic toppicName --delete</span><br></pre></td></tr></table></figure><h2 id="kafka-console-produer-sh-kafka-console-consumer-sh"><a href="#kafka-console-produer-sh-kafka-console-consumer-sh" class="headerlink" title="kafka-console-produer.sh kafka-console-consumer.sh"></a>kafka-console-produer.sh kafka-console-consumer.sh</h2><ul><li>kafka-console-consumer.sh –from-beginning</li></ul><p>示例：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kafka-topics.sh --bootstrap-server xx:9091 --topic toppicName --group myGroup --from-beginning</span><br></pre></td></tr></table></figure><h2 id="kafka-consumer-groups-sh"><a href="#kafka-consumer-groups-sh" class="headerlink" title="kafka-consumer-groups.sh"></a>kafka-consumer-groups.sh</h2><ul><li>bootstrap-server</li><li>command-config</li><li>describe</li><li>group</li><li>list</li></ul><h2 id="DMS监控"><a href="#DMS监控" class="headerlink" title="DMS监控"></a>DMS监控</h2><p>集群&#x2F;节点&#x2F;队列级别监控<br>消费监控</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/614b0a00.html</id>
    <link href="https://blog.moew.xyz/posts/614b0a00.html"/>
    <published>2026-01-21T08:10:26.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>Kafka 架构和机制、常用脚本工具</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="学习" scheme="https://blog.moew.xyz/categories/%E5%AD%A6%E4%B9%A0/"/>
    <category term="kafka" scheme="https://blog.moew.xyz/tags/kafka/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="Kafka-Consumer"><a href="#Kafka-Consumer" class="headerlink" title="Kafka Consumer"></a>Kafka Consumer</h1><h2 id="消费机制"><a href="#消费机制" class="headerlink" title="消费机制"></a>消费机制</h2><p>new consumer &#x2F; old consumer：区别offset保存的地方<br>Group：每个Consumer属于一个Group，可实现广播（发给所有消费组）或单播（组内负载均衡）<br>Rebalance：组内以Topic分区个数进行均衡，组内消费者最多有分区个数的消费者<br>assign模式：手动分配消费分区<br>subscribe模式：自动分配消费分区</p><ol><li>消费者查询一个GroupCordinator</li><li>JoinGroup，分配Partition</li><li>查找所分配Partition的Leader，进行消费</li></ol><h2 id="GroupCordinator职责"><a href="#GroupCordinator职责" class="headerlink" title="GroupCordinator职责"></a>GroupCordinator职责</h2><ul><li>处理JoinGroupRequest、SyncGroupRequest完成Partition分配</li><li>维护_consumer_offset</li><li>通过心跳检查消费者状态</li></ul><p>GroupCordinator选择：<code>__consumers_offsets partition# = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount)</code><br>groupMetadataTopicPartitionCount &#x3D; offsets.topic.num.partitions，默认50<br>该分区leader所在的broker就是被选定的coordinator</p><h2 id="GroupRebalance触发条件"><a href="#GroupRebalance触发条件" class="headerlink" title="GroupRebalance触发条件"></a>GroupRebalance触发条件</h2><ul><li>新Consumer加入Group</li><li>Consumer 退出：主动leave、宕机、网络故障</li><li>Topic分区数变化（扩容）</li><li>Consumer调用unsubscribe</li></ul><h2 id="GroupRebalance流程"><a href="#GroupRebalance流程" class="headerlink" title="GroupRebalance流程"></a>GroupRebalance流程</h2><h2 id="使用规范"><a href="#使用规范" class="headerlink" title="使用规范"></a>使用规范</h2><ol><li>consumer owner线程需要确保不会异常退出，否则相当于宕机并不再发起消费请求，从而阻塞消费</li><li>确保处理完消息再做消息commit，避免拉取消息后消息处理失败（包括拉取消息后宕机），无法重新拉取未处理完成的消息</li><li>consumer避免频繁加入和退出group，否则会导致频繁rebalance阻塞消费。</li><li>consumer数量不能超过topic分区数，否则会有consumer闲置</li><li>consumer需要周期poll维持心跳，否则也会导致频繁退出和加入，后果同3</li><li>consumer拉取的消息本地缓存应限制大小避免OOM</li><li>kafka不能保证消息不重复，业务侧需要保持消息处理幂等性</li><li>消费线程退出要调用consumer的close方法主动退出group，避免同组其他消费者阻塞session.timeout.ms的时间</li></ol>]]>
    </content>
    <id>https://blog.moew.xyz/posts/b2c32b61.html</id>
    <link href="https://blog.moew.xyz/posts/b2c32b61.html"/>
    <published>2026-01-20T18:48:26.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>kafka学习：Consumer</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="学习" scheme="https://blog.moew.xyz/categories/%E5%AD%A6%E4%B9%A0/"/>
    <category term="kafka" scheme="https://blog.moew.xyz/tags/kafka/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h2 id="Producer模型"><a href="#Producer模型" class="headerlink" title="Producer模型"></a>Producer模型</h2><p>Record Accumulator -&gt; Send Thread -&gt; Broker</p><p>KafkaProducer -&gt; Interceptors -&gt; WaitOnMetadata -&gt; KVSerializer -&gt; partioner -&gt; Record Accumulator -&gt; Sender Thread[drain -&gt; sendProduceRequest -&gt; NetworkClient -&gt; KSelector] -&gt; Kafka Broker</p><h2 id="Producer配置"><a href="#Producer配置" class="headerlink" title="Producer配置"></a>Producer配置</h2><h3 id="建议规范"><a href="#建议规范" class="headerlink" title="建议规范"></a>建议规范</h3><p>1.若同步复制ack&#x3D;all(-1)，否则ack&#x3D;1<br>2.retries&#x3D;3 针对可重试异常自动发起重试的次数,默认值为0<br>3.linger.ms&#x3D;0<br>4. Producer JVM内存要够，避免导致发送阻塞<br>5.callback函数不能阻塞，否则会阻塞sender线程</p><p>batch.size默认16384（16KB）推荐262144（256KB）  当batch.size超标或linger.ms超时发送消息</p><h3 id="FIFO保序"><a href="#FIFO保序" class="headerlink" title="FIFO保序"></a>FIFO保序</h3><ol><li>指定partition</li><li>设置请求发送队列长度为1或关闭发送失败重试（至少配置其中一个）</li></ol><p>max.in.flight.requests.per.connection 限制了生产者在等待之前发送的消息确认（ACK）时，可以同时向同一个 Broker 发送的未完成请求数量。<br>高值（如 5）：允许生产者并行发送多个请求，提高吞吐量，但可能增加延迟（因需要等待多个 ACK）。<br>低值（如 1）：确保消息按顺序发送和确认，降低吞吐量但保证顺序。<br>默认值为 5。<br>retries 针对可重试异常自动发起重试的次数,默认值为0</p><p>若启用重试且发送队列长度&gt;1，则可能先发送的请求失败、后发送的请求成功、而后先发送的请求再次尝试并成功，从而乱序</p><h3 id="高吞吐"><a href="#高吞吐" class="headerlink" title="高吞吐"></a>高吞吐</h3><p>topic配置 3分区2副本<br>acks&#x3D;0 or 1</p><h3 id="相对可靠"><a href="#相对可靠" class="headerlink" title="相对可靠"></a>相对可靠</h3><p>3分区3副本<br>#ISR&#x3D;2  min.insync.replicas&#x3D;2<br>acks&#x3D;-1</p><h3 id="高可靠"><a href="#高可靠" class="headerlink" title="高可靠"></a>高可靠</h3><p>3分区3副本<br>#ISR&#x3D;2  min.insync.replicas&#x3D;2<br>flush.messages&#x3D;1  强制刷新写入的最大缓存消息数<br>acks&#x3D;-1 </p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/55fc559a.html</id>
    <link href="https://blog.moew.xyz/posts/55fc559a.html"/>
    <published>2026-01-20T18:48:26.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>kafka学习：Producer</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="论文翻译" scheme="https://blog.moew.xyz/categories/%E8%AE%BA%E6%96%87%E7%BF%BB%E8%AF%91/"/>
    <category term="并发" scheme="https://blog.moew.xyz/categories/%E8%AE%BA%E6%96%87%E7%BF%BB%E8%AF%91/%E5%B9%B6%E5%8F%91/"/>
    <category term="动态分析" scheme="https://blog.moew.xyz/categories/%E8%AE%BA%E6%96%87%E7%BF%BB%E8%AF%91/%E5%B9%B6%E5%8F%91/%E5%8A%A8%E6%80%81%E5%88%86%E6%9E%90/"/>
    <category term="并发检测" scheme="https://blog.moew.xyz/tags/%E5%B9%B6%E5%8F%91%E6%A3%80%E6%B5%8B/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>原文：C.-H. Bae, E. Choi, Y.-K. Jun, and O.-K. Ha, “Lightweight Method for On-the-fly Detection of Multivariable Atomicity Violations,” in 2023 IEEE International Conference on Software Testing, Verification and Validation Workshops (ICSTW), Dublin, Ireland: IEEE, Apr. 2023, pp. 165–171. doi: 10.1109&#x2F;ICSTW58534.2023.00039.</p><p>摘要: 在多线程程序中测试检测实时并发错误（如由多个共享变量引起的原子性违规）具有挑战性，因为需要考虑各种因素，包括变量之间的相关性和访问事件交错。本研究提出了一种在程序执行过程中检测违反原子性的改进方法。该方法采用一种直接的方法，将构成原子性的大量相关变量识别为一个单独的组和一个变量。这种增强型方法与测试工具类似，并使用一组合成程序进行了实验比较，这些程序模拟了七个具有代表性的多变量原子性违规行为的执行过程。结果表明，与测试程序的原始运行和最先进的检测方法相比，执行时间分别增加了 1.07 倍和 1.05 倍。这一结果包括了先前方法未检测到的所有多变量原子性违规行为的检测精度。</p><p>I. 导言</p><p>  并行或多线程程序中的同步是一种强制机制，用于协调各种计算系统中的线程执行和管理共享数据。然而，测试程序以发现由非功能事实（如线程同步）引起的并发错误（如违反原子性）是相当具有挑战性的，因为这需要各种知识和诀窍，如并发线程的交错预判定和共享资源的竞争分析。原子性违规是严重的并发错误之一， 当两个或多个线程同时在一个临界区域内执行时，程序员会认为该临界区域将实现原子性。特别是，如果在预期实现原子性的区域内，对共享变量的访问中至少包括一次写入，就必须对其进行测试和检测，以提高软件系统的可靠性，因为这样会满足竞赛条件，并产生意想不到的后果。</p><p>  以往检测原子性违规行为的研究确定观察到的程序执行是否与串行执行或串行化相同。技术[1]-[4]与支持静态检测的技术相比，即时检测原子性违规的技术会产生更多的误报。但仍可能造成误报和漏报。此外，它们可能无法检测到由多个变量引发的违反原子性的情况，因为它们只考虑了单个变量，而没有分析预期原子执行的关键部分中共享变量之间的相关性。</p><p>  本研究提出了一种轻量级方法，该方法改进了多线程程序执行过程中检测原子性违规的最先进方法–预期不变（AI）[5]算法，考虑了临界区域中组成原子执行的多个变量。所提方法的关键点在于通过将与原子操作相关的多个共享变量分组为一个共享变量来检测违反原子性的情况。原子性违规行为是通过分析共享变量在执行过程中发生的方法来诊断的，这种方法基于程序员所希望的共享变量的接近顺序，反映了通过执行前收集的共享变量的相关性。实验中使用了一组合成程序，模拟了实际应用中出现的七种原子性违规行为的执行过程，比较了所介绍方法的准确性和效率。在实验结果中，与预期不变量不同的是，使用我们的增强型方法的测试工具能正确检测出所有七种情况。此外，与原始执行测试程序相比，该工具平均需要 10.7 次执行时间，仅增加了 6.8%；与预期不变量相比，平均需要 1.05 次执行时间，仅增加了 4.9%，从而有效地检测了由多个变量引起的违反原子性的情况。</p><p>  本文的其余部分安排如下：第 2 节介绍了多线程程序中的并发错误，以及以往检测原子性违规的研究。第 3 节介绍了即时检测方法的设计和操作。第 4 节介绍了拟议方法的实现和实验，第 5 节给出了结论和未来研究。</p><p>II. 背景介绍</p><p>A. 并发错误</p><p>基于共享内存的多线程程序可能会产生并发错误，例如数据竞赛和原子性。在程序执行过程中，由于并发线程对共享变量的访问同步不当而造成的并发违规。众所周知，这些并发错误主要是调试和测试多线程程序时的麻烦缺陷，因为并发线程的交错是非确定性的。</p><p>根据 S. Lu 等人的研究[1]，并发错误可分为死锁错误和非死锁错误。在该研究调查的现实世界应用程序中，约 66% 的错误属于非死锁错误，即同时对共享资源执行 线程操作（如数据竞赛和原子性违规）所导致的竞赛错误。数据竞赛是具有代表性的竞赛错误，当两个线程同 时访问单个共享变量时就会发生，包括在没有适当同步的情况下至少写入一次。违反原子性是指线程在原子代码区域内同时访问共享变量时，意外违反了原子性，从 而发生竞赛错误。</p><p>图 1 是一个在 Apache 应用程序中发生并发错误的示例 [6]。图 1 (a) 是由于在两个并发线程上接近共享变量而导致并发错误的代码区域。Content 和 Content_len 是共享变量，如果 Content 的内容发生变 化，Content_len（保存在 Content 中的值的长度）也要更新。图 1(b)是(a)中的执行图示，箭头表示线程操作，圆圈表示每个共享变量的访问事件，圆圈中的 W 和 R 分别 表示写入和读取访问事件。</p><!-- ![图1]() --><p>图 1 中的执行潜藏着两个共享变量之间的数据竞赛，因为两个不同的线程在没有适当同步的情况下访问了两 个共享变量中的每一个。利用数据竞赛检测技术（如 “发 生前分析”），可以诊断出线程 1 中的 W 和线程 2 中 的 R 分别在两个共享变量之间存在两个数据竞赛。图 2 (a) 显示了通过对每个共享变量使用相同的锁进行同步以 保持预期的访问顺序来消除数据竞赛的结果。</p><p>在 Content_len 受不同锁保护的情况下，竞赛问题依然存在，因为在值更新前可能会发生读取。这是线程 1 中两个 变量的两次写访问事件违反了程序员所希望的原子性的结果。如图 2 (b)所示，通过使用包含两个共享变量的单 个相同锁来保持两个变量的原子性，可以轻松解决图 2 (a) 中的原子性违规问题。</p><!-- ![图2]() --><p>B. 以往的研究 </p><p>众所周知，由于线程交错，并发性错误（如违反原子性）很难重现，而且一般的软件测试方法也很难检测出顺序程序中的错误。因此，并发错误的检测需要基于错误诊断规则或协议的自动程序的帮助。在程序执行过程中检测违反原子性的技术分为基于还原的方法（REM）[7]-[9]、基于访问模式的方法（APM）[5]、[10]-[12]和基于发生关系的方法（HRM）[13]-[15]。REM基于 Lipton 的还原理论[16]，探索访问共享变量的事件的换向属性，并分析原子性，以确定和检测每个线程的访问事件序列是否与获取的执行中定义的模式相匹配。APM 通过分析交错共享变量的访问事件是否与定义的非序列化模式相匹配，来检测违反原子性的情况。最后，HRM 利用预期执行原子性操作的执行区域中访问事件之间的顺序关系，分析和检测共享变量访问事件的冲突。虽然这些即时检测违反原子性的方法比通过静态分析确定的方法更复杂，检测性能也更优越，但它们仍然会引起误报和漏报。</p><p>考虑到现有研究的准确性和开销，AI（预期不变量） [5] 是一种基于 APM 的适用于多线程程序的方法。该研究利用预测试信息诊断并发错误，如违反原子性，并通过停滞诊断出错误的线程来处理并发错误。AI 是 J. Yu 等 人[17]方法的改进版。作者将数据结构中每个内存操作的 顺序定义为 PSet（前置集）。因此，AI 同样将每条指令 的顺序定义为 RPre（远程前置集）和 BSet（归属集）。 RPre 收集与分析共享变量并发访问相关的指令（$I_x$）， 如访问类型（读&#x2F;写）、访问ID、线程ID、代码中的位置和内存地址。RPre 根据指令($I_x$)按以下规则更新信息：</p><ul><li>它访问的地址与 $I_x$ 相同。</li><li>它是在另一个线程中执行的，而$I_x$不属于这个线程。</li><li>包括 RPre($I_x$) 的 BSet 根据动态指令 ($D_x$) 收集满足以下 条件的静态指令 (S ) ：x </li><li>它访问的地址与 $D_x$ 相同。 </li><li>它在另一个线程中执行，而 $D_x$ 不属于该线程。 </li><li>在 $D_x$ 之前访问的 $S_x$ 会被执行并存储。</li></ul>]]>
    </content>
    <id>https://blog.moew.xyz/posts/668e561d.html</id>
    <link href="https://blog.moew.xyz/posts/668e561d.html"/>
    <published>2024-01-30T03:53:14.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>论文翻译：快速检测多变量原子性违规的轻量级方法</title>
    <updated>2026-06-25T01:53:52.150Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="程序分析" scheme="https://blog.moew.xyz/categories/%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90/"/>
    <category term="静态分析" scheme="https://blog.moew.xyz/categories/%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90/%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90/"/>
    <category term="static program analysis" scheme="https://blog.moew.xyz/tags/static-program-analysis/"/>
    <category term="program analysis" scheme="https://blog.moew.xyz/tags/program-analysis/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="数据流分析（data-flow-analysis）"><a href="#数据流分析（data-flow-analysis）" class="headerlink" title="数据流分析（data flow analysis）"></a>数据流分析（data flow analysis）</h1><p>数据流分析是一类分析，其分析对象是控制流图（CFG），产生特定于应用程序的数据结果（application-specific Data）。</p><p>不同的数据流分析应用有着：</p><ul><li>不同的数据抽象</li><li>不同的流安全近似（flow safe-approximation）策略</li><li>不同的传输函数和控制流处理（transfer functions &amp; control-flow handings）</li></ul><p>flow through </p><ul><li>nodes (BBs&#x2F;statements)</li><li>edges (control flows of)</li><li>cfg (a program)</li></ul><h3 id="输入和输出状态"><a href="#输入和输出状态" class="headerlink" title="输入和输出状态"></a>输入和输出状态</h3><p>在每个语句中，前一语句的输出作为当前语句的输入，生成当前的输出。</p><p>在每个数据流分析应用中，我们将每个程序点（program point）与数据流值（data-flow value）关联起来，这个数据流值表示该点可以观察到的所有可能的程序状态的抽象。<br>数据流分析旨在找到一个解，其中包含一组在In[s]和OUT[s]中的以安全近似为导向的约束，对于所有语句s：</p><ul><li>基于语句语义的约束（传输函数，transfer function）</li><li>基于控制流的约束</li></ul><p><img src="/posts/17394cba/input%20&%20output%20states.png"></p><h2 id="传输函数约束的符号表示"><a href="#传输函数约束的符号表示" class="headerlink" title="传输函数约束的符号表示"></a>传输函数约束的符号表示</h2><p>两种类型：</p><ol><li><p>正向分析（forward analysis）<br><code>OUT[s] = f_s(IN[s])</code></p></li><li><p>反向分析：（backward analysis）<br><code>IN[s] = f_s(OUT[s])</code></p></li></ol><p>基本块内的控制流（control flow within a BB）：</p><p><code>IN[s_i+1] = OUT[s_i], for all i=1,2,...,n-1</code><br>基本块之间的控制流（control flow among BBs）：</p><p>参见图表<br><img src="/posts/17394cba/notations%20for%20control%20flow's%20constaints.png" alt="notations for control flow&#39;s constaints"></p><p><img src="/posts/17394cba/meet%20operator%20%5E.png" alt="meet operator ^.png"></p><h2 id="数据流分析应用（Data-flow-analysis-Applications）"><a href="#数据流分析应用（Data-flow-analysis-Applications）" class="headerlink" title="数据流分析应用（Data flow analysis Applications）"></a>数据流分析应用（Data flow analysis Applications）</h2><p>非常基础的3种数据流模式：</p><ol><li><strong>到达定义分析（Reaching Definitions Analysis）</strong></li><li><strong>活跃变量分析（Live Variables Analysis）</strong></li><li><strong>可用表达式分析（Available Expressions Analysis）</strong></li></ol><h3 id="到达定义分析（Reaching-Definitions-Analysis）"><a href="#到达定义分析（Reaching-Definitions-Analysis）" class="headerlink" title="到达定义分析（Reaching Definitions Analysis）"></a>到达定义分析（Reaching Definitions Analysis）</h3><p>A definition d at program point p reaches a point q if there is a path from p to q such that d is not killed along that path<br>在程序点p的定义d能够达到程序点q，是否有一个路径从p到q且d在路径上没有被killed</p><p>给定程序点q，能够检查某变量的值是何时定义的。</p><p>可以用于检查可能的未定义变量，在入口为每个变量v引入dummy定义，如果v的dummy定义能够到达使用v的某个程序点p没有被killed<br>也可以用于常量传播，复制传播上。</p><p>分类：</p><ul><li>forward&#x2F;backward analysis? forward analysis</li><li>must&#x2F;may analysis? may analysis(over apprioximation)</li></ul><p>data flow values&#x2F; facts: all definiton in a program，可以用bit vectors表示 </p><p><img src="/posts/17394cba/reach%20definition%20analysis.png"><br><img src="/posts/17394cba/reach%20definition%20analysis%20example.png"></p><h4 id="algorithm"><a href="#algorithm" class="headerlink" title="algorithm"></a>algorithm</h4><p><img src="/posts/17394cba/algorithm%20of%20reach%20definition%20analysis.png"></p><ul><li>为什么<code>OUT[entry]</code>&#x3D;空集？<ul><li>回想out定义（在这一program point有什么定义可以流到这里）</li></ul></li><li>为什么对各个基本块初始化entry被排除而单独对entry初始化？<ul><li>因为这个算法是一个data algorithm模板，其他的data analysis(特别是must analysis) 中entry和各基本块的初始化会不一样</li></ul></li><li>为什么需要判断OUT是否有变化然后多次迭代？<ul><li>CFG可能包含环，第一次遍历CFG时，某些边可能未初始化。多次迭代是为了获得最终结果。</li></ul></li><li>为什么迭代一定会停止？<br>对于某个基本块B，kill和gen是常量，所以输入不变输出不变。<br>当一个fact（某个位）加到OUT[s]，要么是通过gen添加的，要么是前一个块的fact经过当前块的survivor。<br>当添加更多fact（某个位）时，它们要么被killed，要么流入到<code>OUT[s]</code>中。<br>因此，<code>OUT[s]</code>从来不会变小（其中的位只会从0变成1，或者保持1不变）</li></ul><p>也就是说，如果某位想变0，不可能是kill或gen引起的（因为这俩是常量），只可能是前一个BB的OUT中该位从1变为了0，但这也是不可能的，理由同前，这个过程是递归的，直到入口都不会有人的OUT从1变为了0</p><p>因为fact集合是有限的，所以一定有一趟迭代没有任何东西添加到OUT，所以算法终止</p><h3 id="活跃变量分析（Live-Variables-Analysis）"><a href="#活跃变量分析（Live-Variables-Analysis）" class="headerlink" title="活跃变量分析（Live Variables Analysis）"></a>活跃变量分析（Live Variables Analysis）</h3><p>分析在程序点（program point）p，变量v的值是否在CFG中从p开始的路径中被使用（v在这条路径中使用前不能被重新定义）。如果是，v在p点live。否则，v在p点dead。</p><p>给定程序点p，某变量的值未来是否会使用到</p><p>一个重要用途：可用于为基本块进行寄存器分配。如果我们能分析出某寄存器中的值在以后不会被使用（dead value），我们更倾向于使用这个寄存器。</p><p>data flow values&#x2F; facts: all variable in a program</p><p>分类：</p><ul><li>forward&#x2F;backward analysis? backward analysis</li><li>must&#x2F;may analysis? may analysis</li></ul><p>注意方程中：<br>defb是指这样的变量：基本块中对其定值先于任何对其使用<br>useb是指这样的变量：基本块中对其使用先于任何对其定值</p><p><img src="/posts/17394cba/live%20variables%20analysis.png"><br><img src="/posts/17394cba/live%20variables%20analysis%20explanation.png"><br><img src="/posts/17394cba/live%20variables%20analysis%20example.png"></p><h5 id="algorithm-1"><a href="#algorithm-1" class="headerlink" title="algorithm"></a>algorithm</h5><p><img src="/posts/17394cba/algorithm%20of%20live%20variables%20analysis.png"></p><h2 id="可用表达式分析（Available-Expressions-Analysis）"><a href="#可用表达式分析（Available-Expressions-Analysis）" class="headerlink" title="可用表达式分析（Available Expressions Analysis）"></a>可用表达式分析（Available Expressions Analysis）</h2><p>在程序点p的表达式x op y是可用的，如果</p><ol><li>所有从入口到p一定经过x op y的求值；</li><li>在最后一个x op y的求值以后，没有对x或y的重新定义。</li></ol><p>这个定义意味着在程序点p，我们可以用最后依次x op y的求值结果替换表达式x op y</p><p>主要用途：寻找全局公共子表达式</p><p>分类：</p><ul><li>forward&#x2F;backward analysis? forward analysis</li><li>must&#x2F;may analysis? must analysis(under apprioximation) why? may report an expression as unavailable even if it is truly available</li></ul><p>data flow values&#x2F; facts: all expression in a program</p><p><img src="/posts/17394cba/available%20expression%20analysis.png"><br><img src="/posts/17394cba/available%20expression%20analysis%20example.png"></p><h5 id="algorithm-2"><a href="#algorithm-2" class="headerlink" title="algorithm"></a>algorithm</h5><p>注意基本块初始化为全1（因为后面对于汇聚都是取交集，初始化为0会导致错误）<br>注意汇聚都是取交集（因为must analysis，不能有误报，不能引起错误优化）</p><p><img src="/posts/17394cba/algorithm%20of%20available%20expressions%20analysis.png"></p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p><img src="/posts/17394cba/analysis%20comparison.png"></p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/17394cba.html</id>
    <link href="https://blog.moew.xyz/posts/17394cba.html"/>
    <published>2023-11-23T15:13:36.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>静态分析 - 数据流分析</title>
    <updated>2026-06-25T01:53:52.137Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="程序分析" scheme="https://blog.moew.xyz/categories/%E7%A8%8B%E5%BA%8F%E5%88%86%E6%9E%90/"/>
    <category term="static program analysis" scheme="https://blog.moew.xyz/tags/static-program-analysis/"/>
    <category term="program analysis" scheme="https://blog.moew.xyz/tags/program-analysis/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><h1 id="静态分析导论-Introduction"><a href="#静态分析导论-Introduction" class="headerlink" title="静态分析导论 - Introduction"></a>静态分析导论 - Introduction</h1><p>在软件工程中，静态分析是一种重要的程序分析方法，通过在不运行程序的情况下对其源代码或中间表示进行检查和推理，以获取有关程序行为和性质的信息。本文将介绍静态分析的基本概念以及与之相关的一些重要主题。</p><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>程序的生命周期中的许多关键步骤都涉及到对程序的分析，以便更好地理解、维护和优化代码。静态分析是其中之一，它通过在编译或开发阶段对程序进行检查，提供了一种在运行时难以获得的洞察力。</p><p>静态分析通常包括以下步骤：</p><ol><li><strong>词法分析（Lexical Analysis）</strong>：将源代码转换为令牌流。</li><li><strong>语法分析（Syntax Analysis）</strong>：将令牌流转换为抽象语法树（AST）。</li><li><strong>语义分析（Semantic Analysis）</strong>：对AST进行类型检查和语义检查，生成带有语义信息的修饰AST。</li><li><strong>翻译（Translation）</strong>：将修饰AST翻译为中间表示（IR）。</li><li><strong>静态分析（Static Analysis）</strong>：对IR进行分析，进行诸如代码优化等操作。</li></ol><p>[source] -&gt; scanner(lexcial analysis) -&gt; [tokens] -&gt; parser(syntax analysis) -&gt; [ast] -&gt; type checker(semantic analysis) -&gt;[decorated ast] -&gt; translator -&gt; [IR] -&gt; static analysis, e.g code optimization -&gt; code generator -&gt; [machine code]</p><h2 id="重要概念"><a href="#重要概念" class="headerlink" title="重要概念"></a>重要概念</h2><h3 id="AST-vs-IR"><a href="#AST-vs-IR" class="headerlink" title="AST vs IR"></a>AST vs IR</h3><ul><li><strong>AST（抽象语法树）</strong>：表示源代码的语法结构，是语法分析的输出。</li><li><strong>IR（中间表示）</strong>：在编译器中用于在高级源代码和目标机器代码之间进行转换的数据结构。</li></ul><h3 id="3AC（3-Address-Code）"><a href="#3AC（3-Address-Code）" class="headerlink" title="3AC（3-Address Code）"></a>3AC（3-Address Code）</h3><p>3AC是一种中间表示，每条语句最多包含一个操作符。它包括多种类型的语句，如赋值、条件跳转等。<br>右边最多一个operator( t1&#x3D;a+b, t2&#x3D;t1+3.)</p><ul><li>典型语句类型：<ul><li>x &#x3D; y bop z;</li><li>x &#x3D; uop y;</li><li>x &#x3D; y;</li><li>goto L;</li><li>if x goto L;</li><li>if x rop y goto L</li></ul></li><li>a typed 3AC: soot - jimple<ul><li>for</li><li>do while</li><li>method call</li></ul></li></ul><h3 id="SSA（静态单赋值）"><a href="#SSA（静态单赋值）" class="headerlink" title="SSA（静态单赋值）"></a>SSA（静态单赋值）</h3><p>SSA是一种中间表示的形式，其中每个变量只被赋值（定义）一次。这有助于某些优化和分析，如条件常量传播和全局值编号。</p><h4 id="为什么使用SSA？"><a href="#为什么使用SSA？" class="headerlink" title="为什么使用SSA？"></a>为什么使用SSA？</h4><ul><li>部分精度的流不直接合并到单一变量名中，有助于流不敏感分析获得部分精度。</li><li>显式的定义和使用关系（define-and-use对），使得一些优化更高效（如conditional constant propagation, global value numbering）。</li></ul><h4 id="为什么不使用SSA？"><a href="#为什么不使用SSA？" class="headerlink" title="为什么不使用SSA？"></a>为什么不使用SSA？</h4><ul><li>引入了大量变量和phi函数。</li><li>在生成机器码时可能引入性能问题，因为需要进行复制操作。</li></ul><h2 id="控制流图（CFG）与基本块"><a href="#控制流图（CFG）与基本块" class="headerlink" title="控制流图（CFG）与基本块"></a>控制流图（CFG）与基本块</h2><p>控制流图是程序中基本块之间控制流的图形表示。节点通常是单独的3AC，或者是基本块（Basic Block）</p><p>基本块是一种最基本的控制流单元，它是一个连续的3AC序列，只有一个入口和一个出口。</p><p>从A到B有一条边，当且仅当：</p><ul><li>有一个条件或无条件跳转从A的结束到B的开始（符合的出边）</li><li>B在指令原始序列中紧跟着A，且A的最后不是无条件跳转（不符合的出边）</li></ul><p><img src="/posts/ebcc6fdb/cfg%20edge%20rules.png"><br><img src="/posts/ebcc6fdb/cfg%20edge%20rules%20example.png"></p><h1 id="Basic-Block"><a href="#Basic-Block" class="headerlink" title="Basic Block"></a>Basic Block</h1><p>最长的三地址指令序列满足：只有开始能进入，中间不存在入口；只有结尾能退出，中间不存在出口</p><p><img src="/posts/ebcc6fdb/build%20basic%20blocks.png"><br><img src="/posts/ebcc6fdb/build%20basic%20blocks%20example.png"></p><h1 id="两类分析"><a href="#两类分析" class="headerlink" title="两类分析"></a>两类分析</h1><p>may analysis:<br>outputs information that may be true(over-approximation)</p><p>must analysisL<br>outputs information that must be true(under-approximation)</p><p>over-approximation &amp; under-approximation are both for safety of analysis</p><p><img src="/posts/ebcc6fdb/fp%20&&%20fn.png"></p><p><img src="/posts/ebcc6fdb/sound%20&%20complete.png"></p><p>初步了解了静态分析的基本流程、重要概念以及涉及的一些关键主题。在软件工程中，静态分析是提高代码质量、发现潜在问题和进行优化的强大工具。</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/ebcc6fdb.html</id>
    <link href="https://blog.moew.xyz/posts/ebcc6fdb.html"/>
    <published>2023-11-23T15:05:07.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>静态分析导论 - introduction</title>
    <updated>2026-06-25T01:53:52.127Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="多线程并发" scheme="https://blog.moew.xyz/categories/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91/"/>
    <category term="c++" scheme="https://blog.moew.xyz/tags/c/"/>
    <category term="多线程并发" scheme="https://blog.moew.xyz/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91/"/>
    <category term="atomic" scheme="https://blog.moew.xyz/tags/atomic/"/>
    <category term="memory order" scheme="https://blog.moew.xyz/tags/memory-order/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>本文以c++内存模型为参考，但也适用于大多数其他语言（许多语言使用了类似c++的模型）。</p><h1 id="同步发生-synchronizes-with"><a href="#同步发生-synchronizes-with" class="headerlink" title="同步发生(synchronizes-with)"></a>同步发生(synchronizes-with)</h1><p>“同步发生”只能在原子类型之间进行操作。例如对一个数据结构进行操作(对互斥量上锁)，如果数据结构包含有原子类型，并且操作内部执行了一定的原子操作，那么这些操作就是同步发生关系。从根本上说，这种关系只能来源于对原子类型的操作。</p><p>“同步发生”的基本想法是：在变量x进行适当标记的原子写操作W，同步与对x进行适当标记的原子读操作，读取的是W操作写入的内容；或是在W之后，同一线程上的原子写操作对x写入的值；亦或是任意线程对x的一系列原子读-改-写操作(例如，fetch_add()或compare_exchange_weak())。这里，第一个线程读取到的值是W操作写入的。</p><p>先将“适当的标记”放在一边，因为所有对原子类型的操作，默认都是适当标记的。这实际上就是：如果线程A存储了一个值，并且线程B读取了这个值，线程A的存储操作与线程B的载入操作就是同步发生的关系。</p><figure class="highlight cpp"><table><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="comment">// code fragment 1:</span></span><br><span class="line"><span class="comment">// 当data_ready①为true，写操作就会与读操作同步，建立一个“先行发生”关系。</span></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; data;</span><br><span class="line"><span class="function">std::atomic&lt;<span class="type">bool</span>&gt; <span class="title">data_ready</span><span class="params">(<span class="literal">false</span>)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">reader_thread</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(!data_ready.<span class="built_in">load</span>())  <span class="comment">// 1</span></span><br><span class="line">  &#123;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep</span>(std::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">  &#125;</span><br><span class="line">  std::cout&lt;&lt;<span class="string">&quot;The answer=&quot;</span>&lt;&lt;data[<span class="number">0</span>]&lt;&lt;<span class="string">&quot;\m&quot;</span>;  <span class="comment">// 2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">writer_thread</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  data.<span class="built_in">push_back</span>(<span class="number">42</span>);  <span class="comment">// 3</span></span><br><span class="line">  data_ready=<span class="literal">true</span>;  <span class="comment">// 4</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="先行发生-happens-before"><a href="#先行发生-happens-before" class="headerlink" title="先行发生(happens-before)"></a>先行发生(happens-before)</h1><p>“先行发生”关系是一个程序中，基本构建块的操作顺序；它指定了某个操作去影响另一个操作。对于单线程来说，就简单了：当一个操作排在另一个之后，那么这个操作就是先行执行的。这意味着，如果源码中操作A发生在操作B之前，那么A就先行于B发生。例如对于前一个程序，对data的写入③先于对data_ready④的写入。</p><p>如果操作在同时发生，因为操作间无序执行，通常情况下，它们就没有先行关系了。这就是另一种排序未被指定的情况。下面的程序会输出“1，2”或“2，1”，因为两个get_num()的执行顺序未被指定。</p><figure class="highlight cpp"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// code fragment 2:</span></span><br><span class="line"><span class="comment">// 对于参数中的函数调用顺序是未指定顺序的</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  std::cout&lt;&lt;a&lt;&lt;”,”&lt;&lt;b&lt;&lt;std::endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">get_num</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="type">static</span> <span class="type">int</span> i=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">return</span> ++i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="built_in">foo</span>(<span class="built_in">get_num</span>(),<span class="built_in">get_num</span>());  <span class="comment">// 无序调用get_num()</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="线程间的互相作用：线程间的先行"><a href="#线程间的互相作用：线程间的先行" class="headerlink" title="线程间的互相作用：线程间的先行"></a>线程间的互相作用：线程间的先行</h1><p>如果操作A在线程上，并且线程先行于另一线程上的操作B，那么A就先行于B。这也没什么，你只是添加了一个新关系。</p><p>从基本层面上讲，线程间的先行比较简单，并且依赖与同步关系：如果操作A在一个线程上，与另一个线程上的操作B同步，那么A就线程间先行于B。这同样是一个传递关系：如果A线程间先行于B，并且B线程间先行于C，那么A就线程间先行于C。你可以回看一下第一个程序。</p><p>线程间先行可以与排序先行关系相结合：如果操作A排序先行于操作B，并且操作B线程间先行于操作C，那么A线程间先行于C。同样的，如果A同步于B，并且B排序先于C，那么A线程间先行于C。两者的结合，意味着当你对数据进行一系列修改(单线程)时，为线程后续执行C，只需要对可见数据进行一次同步。</p><p>这些是线程间强制排序操作的关键规则，也是让第一段程序正常运行的因素。并在数据依赖上有一些细微的差别，你马上就会看到。为了让你理解这些差别，需要讲述一下原子操作使用的内存排序标签，以及这些标签和同步发生之间的联系。</p><h1 id="原子操作的内存顺序"><a href="#原子操作的内存顺序" class="headerlink" title="原子操作的内存顺序"></a>原子操作的内存顺序</h1><p>六个内存序列选项可应用于对原子类型的操作：</p><ul><li>memory_order_relaxed,</li><li>memory_order_consume</li><li>memory_order_acquire</li><li>memory_order_release</li><li>memory_order_acq_rel</li><li>memory_order_seq_cst（默认顺序）。</li></ul><p>虽然有六个选项，但是它们仅代表三种内存模型：</p><ul><li>排序一致序列(sequentially consistent)</li><li>获取-释放序列(memory_order_consume, memory_order_acquire, memory_order_release和memory_order_acq_rel)</li><li>自由序列(memory_order_relaxed)。</li></ul><p>这些不同的内存序列模型，在不同的CPU架构下，功耗是不一样的。<br>例如，基于处理器架构的可视化精细操作的系统，比起其他系统，添加的同步指令可被排序一致序列使用(在获取-释放序列和自由序列之前)，或被获取-释放序列调用(在自由序列之前)。如果这些系统有多个处理器，这些额外添加的同步指令可能会消耗大量的时间，从而降低系统整体的性能。<br>另一方面，CPU使用的是x86或x86-64架构(例如，使用Intel或AMD处理器的台式电脑)，使用这种架构的CPU不需要任何对获取-释放序列添加额外的指令(没有保证原子性的必要了)，并且，即使是排序一致序列，对于加载操作也不需要任何特殊的处理，不过在进行存储时，有点额外的消耗。</p><p>不同种类的内存序列模型，允许专家利用其提升与更细粒度排序相关操作的性能。当默认使用排序一致序列(相较于其他序列，它是最简单的)时，对于在那些不大重要的情况下是有利的。</p><h2 id="排序一致序列"><a href="#排序一致序列" class="headerlink" title="排序一致序列"></a>排序一致序列</h2><p>默认序列命名为排序一致，因为程序中的行为从任意角度去看，序列顺序都保持一致。如果原子类型实例上的所有操作都是序列一致的，那么一个多线程程序的行为，就以某种特殊的排序执行，好像单线程那样。这是目前来看，最容易理解的内存序列，这也就是将其设置为默认的原因：所有线程都必须了解，不同的操作也遵守相同的顺序。</p><p>因为其简单的行为，可以使用原子变量进行编写。通过不同的线程，你可以写出所有序列上可能的操作，这样就可以消除那些不一致，以及验证你代码的行为是否与预期相符。</p><p>这也就意味着，<strong>所有操作都不能重排序</strong>；<em>如果你的代码，在一个线程中，将一个操作放在另一个操作前面，那么这个顺序就必须让其他所有的线程所了解。</em></p><figure class="highlight cpp"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;assert.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::atomic&lt;<span class="type">bool</span>&gt; x,y;</span><br><span class="line">std::atomic&lt;<span class="type">int</span>&gt; z;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_x</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  x.<span class="built_in">store</span>(<span class="literal">true</span>,std::memory_order_seq_cst);  <span class="comment">// 1</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_y</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  y.<span class="built_in">store</span>(<span class="literal">true</span>,std::memory_order_seq_cst);  <span class="comment">// 2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">read_x_then_y</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(!x.<span class="built_in">load</span>(std::memory_order_seq_cst));</span><br><span class="line">  <span class="keyword">if</span>(y.<span class="built_in">load</span>(std::memory_order_seq_cst))  <span class="comment">// 3</span></span><br><span class="line">    ++z;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">read_y_then_x</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(!y.<span class="built_in">load</span>(std::memory_order_seq_cst));</span><br><span class="line">  <span class="keyword">if</span>(x.<span class="built_in">load</span>(std::memory_order_seq_cst))  <span class="comment">// 4</span></span><br><span class="line">    ++z;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  x=<span class="literal">false</span>;</span><br><span class="line">  y=<span class="literal">false</span>;</span><br><span class="line">  z=<span class="number">0</span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">a</span><span class="params">(write_x)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">b</span><span class="params">(write_y)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">c</span><span class="params">(read_x_then_y)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">d</span><span class="params">(read_y_then_x)</span></span>;</span><br><span class="line">  a.<span class="built_in">join</span>();</span><br><span class="line">  b.<span class="built_in">join</span>();</span><br><span class="line">  c.<span class="built_in">join</span>();</span><br><span class="line">  d.<span class="built_in">join</span>();</span><br><span class="line">  <span class="built_in">assert</span>(z.<span class="built_in">load</span>()!=<span class="number">0</span>);  <span class="comment">// 5</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>assert⑤语句是永远不会触发的。</p><p>如果在read_x_then_y中加载y③返回false，那是因为存储x的操作肯定发生在存储y的操作之前，那么在这种情况下在read_y_then_x中加载x④必定会返回true，因为while循环能保证在某一时刻y是true。<br>因为memory_order_seq_cst的语义需要一个单全序将所有操作都标记为memory_order_seq_cst，这就暗示着“加载y并返回false③”与“存储y①”的操作，有一个确定的顺序。只有一个全序时，如果一个线程看到x&#x3D;&#x3D;true，随后又看到y&#x3D;&#x3D;false，这就意味着在总序列中存储x的操作发生在存储y的操作之前。</p><p>只有一个全序时，如果一个线程看到x&#x3D;&#x3D;true，随后又看到y&#x3D;&#x3D;false，这就意味着在总序列中存储x的操作发生在存储y的操作之前。</p><p>当然，因为所有事情都是对称的，所以就有可能以其他方式发生，比如，加载x④的操作返回false，或强制加载y③的操作返回true。在这两种情况下，z都等于1。当两个加载操作都返回true，z就等于2，所以任何情况下，z都不能是0。</p><p>当read_x_then_y知道x为true，并且y为false，那么这些操作就有“先发执行”关系了，如图所示。</p><p><img src="/posts/e84a32e0/5-3.png" alt="序列一致与先发执行"></p><p>序列一致是最简单、直观的序列，但是他也是最昂贵的内存序列，因为它需要对所有线程进行全局同步。在一个多处理系统上，这就需要处理期间进行大量并且费时的信息交换。</p><p>为了避免这种同步消耗，你需要走出序列一致的世界，并且考虑使用其他内存序列。</p><h2 id="非排序一致内存模型"><a href="#非排序一致内存模型" class="headerlink" title="非排序一致内存模型"></a>非排序一致内存模型</h2><p>当你踏出序列一致的世界，所有事情就开始变的复杂。可能最需要处理的问题就是：再也不会有全局的序列了。这就意味着不同线程看到相同操作，不一定有着相同的顺序，还有对于不同线程的操作，都会整齐的，一个接着另一个执行的想法是需要摒弃的。不仅是你有没有考虑事情真的同时发生的问题，还有线程没必要去保证一致性。为了写出(或仅是了解)任何一段使用非默认内存序列的代码，要想做这件事情，那么之前的那句话就是至关重要的。你要知道，这不仅仅是编译器可以重新排列指令的问题。即使线程运行相同的代码，它们都能拒绝遵循事件发生的顺序，因为操作在其他线程上没有明确的顺序限制；因为不同的CPU缓存和内部缓冲区，在同样的存储空间中可以存储不同的值。这非常重要，这里我再重申一遍：线程没必要去保证一致性。</p><p>不仅是要摒弃交错执行操作的想法，你还要放弃使用编译器或处理器重排指令的想法。<strong>在没有明确的顺序限制下，唯一的要求就是，所有线程都要统一对每一个独立变量的修改顺序。对不同变量的操作可以体现在不同线程的不同序列上，提供的值要与任意附加顺序限制保持一致。</strong></p><p>踏出排序一致世界后，最好的示范就是使用memory_order_relaxed对所有操作进行约束。如果你已经对其有所了解，那么你可以跳到获取-释放序列继续阅读，获取-释放序列允许你选择在操作间引入顺序关系(并且收回你的理智)。</p><h2 id="自由序列"><a href="#自由序列" class="headerlink" title="自由序列"></a>自由序列</h2><p>在原子类型上的操作以自由序列执行，没有任何同步关系。在同一线程中对于同一变量的操作还是服从先发执行的关系，但是这里不同线程几乎不需要相对的顺序。</p><p>唯一的要求是，在访问同一线程中的单个原子变量不能重排序；当一个给定线程已经看到一个原子变量的特定值，线程随后的读操作就不会去检索变量较早的那个值。</p><p>当使用memory_order_relaxed，就不需要任何额外的同步，对于每个变量的修改顺序只是线程间共享的事情。</p><figure class="highlight cpp"><table><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="comment">// 非限制操作只有非常少的顺序要求</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;assert.h&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::atomic&lt;<span class="type">bool</span>&gt; x,y;</span><br><span class="line">std::atomic&lt;<span class="type">int</span>&gt; z;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">write_x_then_y</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  x.<span class="built_in">store</span>(<span class="literal">true</span>,std::memory_order_relaxed);  <span class="comment">// 1</span></span><br><span class="line">  y.<span class="built_in">store</span>(<span class="literal">true</span>,std::memory_order_relaxed);  <span class="comment">// 2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">read_y_then_x</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(!y.<span class="built_in">load</span>(std::memory_order_relaxed));  <span class="comment">// 3</span></span><br><span class="line">  <span class="keyword">if</span>(x.<span class="built_in">load</span>(std::memory_order_relaxed))  <span class="comment">// 4</span></span><br><span class="line">    ++z;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  x=<span class="literal">false</span>;</span><br><span class="line">  y=<span class="literal">false</span>;</span><br><span class="line">  z=<span class="number">0</span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">a</span><span class="params">(write_x_then_y)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">b</span><span class="params">(read_y_then_x)</span></span>;</span><br><span class="line">  a.<span class="built_in">join</span>();</span><br><span class="line">  b.<span class="built_in">join</span>();</span><br><span class="line">  <span class="built_in">assert</span>(z.<span class="built_in">load</span>()!=<span class="number">0</span>);  <span class="comment">// 5</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这次assert⑤可能会触发，因为加载x的操作④可能读取到false，即使加载y的操作③读取到true，并且存储x的操作①先发与存储y的操作②。x和y是两个不同的变量，所以这里没有顺序去保证每个操作产生相关值的可见性。</p><p>非限制操作对于不同变量可以自由重排序，只要它们服从任意的先发执行关系即可(比如，在同一线程中)。它们不会引入同步相关的顺序。清单5.5中的先发执行关系如图5.4所示(只是其中一个可能的结果)。尽管，在不同的存储&#x2F;加载操作间有着先发执行关系，这里不是在一对存储于载入之间了，所以载入操作可以看到“违反”顺序的存储操作。</p><p><img src="/posts/e84a32e0/5-4.png" alt="非限制原子操作与先发执行"></p><p>让我们来看一个略微复杂的例子，其有三个变量和五个线程。</p><figure class="highlight cpp"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">你拥有三个全局原子变量①和五个线程。每一个线程循环10次，使用memory_order_relaxed读取三个原子变量的值，并且将它们存储在一个数组上。其中三个线程每次通过循环④来更新其中一个原子变量，这时剩下的两个线程就只负责读取。当所有线程都“加入”，就能打印出来每个线程存到数组上的值了。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">std::atomic&lt;<span class="type">int</span>&gt; <span class="title">x</span><span class="params">(<span class="number">0</span>)</span>,<span class="title">y</span><span class="params">(<span class="number">0</span>)</span>,<span class="title">z</span><span class="params">(<span class="number">0</span>)</span></span>;  <span class="comment">// 1</span></span><br><span class="line"><span class="function">std::atomic&lt;<span class="type">bool</span>&gt; <span class="title">go</span><span class="params">(<span class="literal">false</span>)</span></span>;  <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line"><span class="type">unsigned</span> <span class="type">const</span> loop_count=<span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">read_values</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="type">int</span> x,y,z;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">read_values values1[loop_count];</span><br><span class="line">read_values values2[loop_count];</span><br><span class="line">read_values values3[loop_count];</span><br><span class="line">read_values values4[loop_count];</span><br><span class="line">read_values values5[loop_count];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">increment</span><span class="params">(std::atomic&lt;<span class="type">int</span>&gt;* var_to_inc,read_values* values)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(!go)</span><br><span class="line">    std::this_thread::<span class="built_in">yield</span>();  <span class="comment">// 3 自旋，等待信号</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">unsigned</span> i=<span class="number">0</span>;i&lt;loop_count;++i)</span><br><span class="line">  &#123;</span><br><span class="line">    values[i].x=x.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line">    values[i].y=y.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line">    values[i].z=z.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line">    var_to_inc-&gt;<span class="built_in">store</span>(i<span class="number">+1</span>,std::memory_order_relaxed);  <span class="comment">// 4</span></span><br><span class="line">    std::this_thread::<span class="built_in">yield</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">read_vals</span><span class="params">(read_values* values)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(!go)</span><br><span class="line">    std::this_thread::<span class="built_in">yield</span>(); <span class="comment">// 5 自旋，等待信号</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">unsigned</span> i=<span class="number">0</span>;i&lt;loop_count;++i)</span><br><span class="line">  &#123;</span><br><span class="line">    values[i].x=x.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line">    values[i].y=y.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line">    values[i].z=z.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line">    std::this_thread::<span class="built_in">yield</span>();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">(read_values* v)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">unsigned</span> i=<span class="number">0</span>;i&lt;loop_count;++i)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span>(i)</span><br><span class="line">      std::cout&lt;&lt;<span class="string">&quot;,&quot;</span>;</span><br><span class="line">    std::cout&lt;&lt;<span class="string">&quot;(&quot;</span>&lt;&lt;v[i].x&lt;&lt;<span class="string">&quot;,&quot;</span>&lt;&lt;v[i].y&lt;&lt;<span class="string">&quot;,&quot;</span>&lt;&lt;v[i].z&lt;&lt;<span class="string">&quot;)&quot;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  std::cout&lt;&lt;std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="function">std::thread <span class="title">t1</span><span class="params">(increment,&amp;x,values1)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">t2</span><span class="params">(increment,&amp;y,values2)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">t3</span><span class="params">(increment,&amp;z,values3)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">t4</span><span class="params">(read_vals,values4)</span></span>;</span><br><span class="line">  <span class="function">std::thread <span class="title">t5</span><span class="params">(read_vals,values5)</span></span>;</span><br><span class="line"></span><br><span class="line">  go=<span class="literal">true</span>;  <span class="comment">// 6 开始执行主循环的信号</span></span><br><span class="line"></span><br><span class="line">  t<span class="number">5.</span><span class="built_in">join</span>();</span><br><span class="line">  t<span class="number">4.</span><span class="built_in">join</span>();</span><br><span class="line">  t<span class="number">3.</span><span class="built_in">join</span>();</span><br><span class="line">  t<span class="number">2.</span><span class="built_in">join</span>();</span><br><span class="line">  t<span class="number">1.</span><span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">  <span class="built_in">print</span>(values1);  <span class="comment">// 7 打印最终结果</span></span><br><span class="line">  <span class="built_in">print</span>(values2);</span><br><span class="line">  <span class="built_in">print</span>(values3);</span><br><span class="line">  <span class="built_in">print</span>(values4);</span><br><span class="line">  <span class="built_in">print</span>(values5);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>程序一种可能的输出为：</p><figure class="highlight plaintext"><table><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">(0,0,0),(1,0,0),(2,0,0),(3,0,0),(4,0,0),(5,7,0),(6,7,8),(7,9,8),(8,9,8),(9,9,10)</span><br><span class="line">(0,0,0),(0,1,0),(0,2,0),(1,3,5),(8,4,5),(8,5,5),(8,6,6),(8,7,9),(10,8,9),(10,9,10)</span><br><span class="line">(0,0,0),(0,0,1),(0,0,2),(0,0,3),(0,0,4),(0,0,5),(0,0,6),(0,0,7),(0,0,8),(0,0,9)</span><br><span class="line">(1,3,0),(2,3,0),(2,4,1),(3,6,4),(3,9,5),(5,10,6),(5,10,8),(5,10,10),(9,10,10),(10,10,10)</span><br><span class="line">(0,0,0),(0,0,0),(0,0,0),(6,3,7),(6,5,7),(7,7,7),(7,8,7),(8,8,7),(8,8,9),(8,8,9)</span><br></pre></td></tr></table></figure><p>前三行中线程都做了更新，后两行线程只是做读取。每三个值都是一组x，y和z，并按照这样的顺序依次循环。对于输出，需要注意的一些事是：</p><ol><li>第一组值中x增1，第二组值中y增1，并且第三组中z增1。</li><li>x元素只在给定集中增加，y和z也一样，但是增加是不均匀的，并且相对顺序在所有线程中都不同。</li><li>线程3看不到x或y的任何更新；他能看到的只有z的更新。这并不妨碍别的线程观察z的更新，并同时观察x和y的更新。</li></ol><p>对于非限制操作，这个结果是合法的，但是不是唯一合法的输出。任意组值都用三个变量保持一致，值从0到10依次递增，并且线程递增给定变量，所以打印出来的值在0到10的范围内都是合法的。</p><p>注意：在各变量都是自增的前提下，即使线程1观察到x&#x3D;3时y仍然为0，但不影响线程4仍然有机会观察到x&#x3D;1时y已经自增到3。</p><h2 id="了解自由排序"><a href="#了解自由排序" class="headerlink" title="了解自由排序"></a>了解自由排序</h2><p>为了了解自由序列是如何工作的，先将每一个变量想象成一个在独立房间中拿着记事本的人。他的记事本上是一组值的列表。你可以通过打电话的方式让他给你一个值，或让他写下一个新值。如果你告诉他写下一个新值，他会将这个新值写在表的最后。如果你让他给你一个值，他会从列表中读取一个值给你。</p><p>在你第一次与这个人交谈时，如果你问他要一个值，他可能会给你现在列表中的任意值。如果之后你再问他要一个值，它可能会再给你同一个值，或将列表后面的值给你，他不会给你列表上端的值。如果你让他写一个值，并且随后再问他要一个值，他要不就给你你刚告诉他的那个值，要不就是一个列表下端的值。</p><p>试想当他的笔记本上开始有5，10，23，3，1，2这几个数。如果你问他索要一个值，你可能获取这几个数中的任意一个。如果他给你10，那么下次再问他要值的时候可能会再给你10，或者10后面的数，但绝对不会是5。如果那你问他要了五次，他就可能回答“10，10，1，2，2”。如果你让他写下42，他将会把这个值添加在列表的最后。如果你再问他要值，他可能会告诉你“42”，直到有其他值写在了后面并且他认为他愿意将那个数告诉你。</p><p>现在，想象你有个朋友叫Carl，他也有那个计数员的电话。Carl也可以打电话给计算员，让他写下一个值或获取一个值，他对Carl回应的规则和你是一样的。他只有一部电话，所以他一次只能处理一个人的请求，所以他记事本上的列表是一个简单的列表。但是，你让他写下一个新值的时候，不意味着他会将这个消息告诉Carl，反之亦然。如果Carl从他那里获取一个值“23”，之后因为你告诉他写下42，这不意味着下次他会将这件事告诉Carl。他可能会告诉Carl任意一个值，23，3，1，2，42亦或是67(是Fred在你之后告诉他的)。他会很高兴的告诉Carl“23，3，3，1，67”，与你告诉他的值完全不一致。这就像它在使用便签跟踪告诉每个人的数，就像图5.5那样。</p><p>现在，想象一下，不仅仅只有一个人在房间里，而是在一个小农场里，每个人都有一部电话和一个笔记本。这就是我们的原子变量。每一个变量拥有他们自己的修改顺序(笔记上的简单数值列表)，但是每个原子变量之间没有任何关系。如果每一个调用者(你，Carl，Anne，Dave和Fred)是一个线程，那么对每个操作使用memory_order_relaxed你就会得到上面的结果。这里还有些事情你可以告诉在小房子的人，例如，“写下这个值，并且告诉我现在列表中的最后一个值”(exchange)，或“写下这个值，当列表的最后一个值为某值；如果不是，告诉我看我是不是猜对了”(compare_exchange_strong)，但是这都不影响一般性原则。</p><p>如果你仔细想想清单5.5的逻辑，那么write_x_then_y就像某人打电话给房子x里的人，并且告诉他写下true，之后打电话给在y房间的另一个人，告诉他写下true。线程反复执行调用read_y_then_x，就像打电话给房间y的人问他要值，直到要到true，然后打电话给房间x的，继续问他要值。在x房间中的人有义务告诉你在他列表中任意指定的值，他也是有权利所false的。</p><p>这就让自由的原子操作变得难以处理。他们必须与原子操作结合使用，这些原子操作必须有较强的排序语义，为了让内部线程同步变得更有用。我强烈建议避免自由的原子操作，除非它们是硬性要求的，并且在使用它们的时候需要十二分的谨慎。给出的不直观的结果，就像是清单5.5中使用双线程和双变量的结果一样，不难想象在有更多线程和更多变量时，其会变的更加复杂。</p><p>要想获取额外的同步，且不使用全局排序一致，可以使用获取-释放序列(acquire-release ordering)。</p><h2 id="释放序列"><a href="#释放序列" class="headerlink" title="释放序列"></a>释放序列</h2><p>释放序列(release sequence)是一种内存顺序，用于描述对共享数据的写入操作的顺序。假设有两个线程，一个执行写入操作，另一个执行读取操作。释放序列确保在写入操作之前的所有读取和写入操作都在写入操作之前完成，从而确保了一致性。</p><p>释放序列是针对具有memory_order_release内存顺序的写入操作的概念。当一个线程执行一个具有释放顺序的写入操作时，它确保在这个写入操作之前的所有写入和读取操作都在这个写入操作之前完成。这样可以防止编译器和处理器对写入操作的重新排序，确保其他线程在读取这个写入的数据时能够看到写入操作之前的所有更新。</p><h2 id="一些补充概念"><a href="#一些补充概念" class="headerlink" title="一些补充概念"></a>一些补充概念</h2><p>总序（total order）是指一个对于所有的操作，都存在一个全局的一致的执行顺序。即，任意两个操作都可以被比较出一个先后顺序。这意味着所有的操作都有一个明确定义的顺序，不会存在模糊或不一致的情况。<br>单一总序（Single total order）是 总序 的一个特例，它要求所有的操作都按照它们在程序中出现的顺序执行。<br>修改顺序（Modification order） 是指在多线程环境中，每个变量（或对象）的修改操作有一个明确定义的顺序。这意味着如果一个线程对变量进行了修改，那么其他线程对<strong>同一变量的修改操作</strong>会按照一定的顺序进行：并且在同一线程上读取对象的操作，要不返回一个已写入的值，要不在对象的修改顺序后(也就是在读取后)再写入的另一个值。<br>总修改序（Total Modification Order）是对 修改顺序 的一种强化，它要求对所有变量的修改操作都存在一个全局的一致的执行顺序。保证了在整个系统中对于所有变量的修改都有一个确定的顺序。</p><p>Releax 序、Rel-Acq 序 都提供修改顺序保证（注意是对于同一变量的），不提供总序保证，不提供总修改顺序保证。(Rel-Acq 序比Releax序多的是，提供Rel-Acq成对操作之间的顺序保证)<br>顺序一致性排序拥有总序、修改顺序、总修改序。</p><h1 id="引用和致谢"><a href="#引用和致谢" class="headerlink" title="引用和致谢"></a>引用和致谢</h1><p>本文极大程度引用和借鉴了《C++ Concurrency In Action》，特别是其中文翻译版的内容：<br><a href="http://shouce.jb51.net/cpp_concurrency_in_action/content/chapter5/5.3-chinese.html">http://shouce.jb51.net/cpp_concurrency_in_action/content/chapter5/5.3-chinese.html</a></p><p>感谢该书作者和译者。</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/e84a32e0.html</id>
    <link href="https://blog.moew.xyz/posts/e84a32e0.html"/>
    <published>2023-11-18T14:57:56.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>memory order</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="技术实践" scheme="https://blog.moew.xyz/categories/%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5/"/>
    <category term="记录" scheme="https://blog.moew.xyz/tags/%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>wsl1适用版：</p><figure class="highlight bash"><table><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">hostip=<span class="string">&quot;127.0.0.1&quot;</span></span><br><span class="line">socks_hostport=10808</span><br><span class="line">http_hostport=10809</span><br><span class="line"><span class="built_in">export</span> all_proxy=<span class="string">&quot;socks5://<span class="variable">$&#123;hostip&#125;</span>:<span class="variable">$&#123;socks_hostport&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span> http_proxy=<span class="string">&quot;http://<span class="variable">$&#123;hostip&#125;</span>:<span class="variable">$&#123;http_hostport&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span> https_proxy=<span class="string">&quot;http://<span class="variable">$&#123;hostip&#125;</span>:<span class="variable">$&#123;http_hostport&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span> ftp_proxy=<span class="variable">$http_proxy</span></span><br><span class="line"><span class="built_in">export</span> rsync_proxy=<span class="variable">$http_proxy</span></span><br><span class="line"><span class="built_in">export</span> ALL_PROXY=<span class="variable">$all_proxy</span></span><br><span class="line"><span class="built_in">export</span> HTTP_PROXY=<span class="variable">$http_proxy</span></span><br><span class="line"><span class="built_in">export</span> HTTPS_PROXY=<span class="variable">$https_proxy</span></span><br><span class="line"><span class="built_in">export</span> FTP_PROXY=<span class="variable">$ftp_proxy</span></span><br><span class="line"><span class="built_in">export</span> RSYNC_PROXY=<span class="variable">$rsync_proxy</span></span><br></pre></td></tr></table></figure><p>特点：<br>因为wsl1在逻辑上与宿主机属于相同的主机，ip直接使用127.0.0.1</p><p>wsl2适用版：</p><figure class="highlight bash"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># Set network proxy</span></span><br><span class="line">hostip=$(ip route | grep default | awk <span class="string">&#x27;&#123;print $3&#125;&#x27;</span>)</span><br><span class="line">socks_hostport=10810</span><br><span class="line">http_hostport=10811</span><br><span class="line"><span class="built_in">export</span> all_proxy=<span class="string">&quot;socks5://<span class="variable">$&#123;hostip&#125;</span>:<span class="variable">$&#123;socks_hostport&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span> http_proxy=<span class="string">&quot;http://<span class="variable">$&#123;hostip&#125;</span>:<span class="variable">$&#123;http_hostport&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span> https_proxy=<span class="string">&quot;http://<span class="variable">$&#123;hostip&#125;</span>:<span class="variable">$&#123;http_hostport&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span> ftp_proxy=<span class="variable">$http_proxy</span></span><br><span class="line"><span class="built_in">export</span> rsync_proxy=<span class="variable">$http_proxy</span></span><br><span class="line"><span class="built_in">export</span> ALL_PROXY=<span class="variable">$all_proxy</span></span><br><span class="line"><span class="built_in">export</span> HTTP_PROXY=<span class="variable">$http_proxy</span></span><br><span class="line"><span class="built_in">export</span> HTTPS_PROXY=<span class="variable">$https_proxy</span></span><br><span class="line"><span class="built_in">export</span> FTP_PROXY=<span class="variable">$ftp_proxy</span></span><br><span class="line"><span class="built_in">export</span> RSYNC_PROXY=<span class="variable">$rsync_proxy</span></span><br><span class="line"><span class="built_in">export</span> no_proxy=<span class="string">&quot;localhost,127.0.0.1&quot;</span></span><br></pre></td></tr></table></figure><p>特点：<br>因为wsl2在逻辑上与宿主机属于不同的主机，<br>使用<code>ip route | grep default | awk &#39;{print $3}&#39;</code>获取宿主机ip<br>另外需要设置no_proxy，否则wsl2运行的程序试图访问wsl2本机时也会被错误转发到宿主机代理然后访问成宿主机，导致一些程序出错。</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/e0a33a48.html</id>
    <link href="https://blog.moew.xyz/posts/e0a33a48.html"/>
    <published>2023-09-06T08:53:12.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>wsl配置代理</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="技术实践" scheme="https://blog.moew.xyz/categories/%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5/"/>
    <category term="记录" scheme="https://blog.moew.xyz/tags/%E8%AE%B0%E5%BD%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>windows用户：</p><figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netsh interface tcp <span class="built_in">set</span> global timestamps=enabled</span><br></pre></td></tr></table></figure><p>linux用户：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl -w net.ipv4.tcp_timestamps=1</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.moew.xyz/posts/a8d904d5.html</id>
    <link href="https://blog.moew.xyz/posts/a8d904d5.html"/>
    <published>2023-08-14T09:47:04.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>TCP开启timestamps</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="折腾记录" scheme="https://blog.moew.xyz/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="Windows" scheme="https://blog.moew.xyz/tags/Windows/"/>
    <category term="巨硬特技" scheme="https://blog.moew.xyz/tags/%E5%B7%A8%E7%A1%AC%E7%89%B9%E6%8A%80/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>首先说结论，这个问题（在我所遇到的情况中）大多数是由于加速器软件或者全局代理类软件修改了系统的Winsock来全局加载代理导致的，而WSL2与其不兼容，所以产生 0x8007273d 错误。</p><p>你可以通过简单地命令行中运行<code>netsh winsock reset</code>（管理员权限，本文后续也默认在管理员权限cmd中运行）来重置Winsock，然后 WSL2 就可以正常工作了。<br>但也这会使 Proxifier等（使用 winsock 的软件）不再能工作。</p><p>如果你希望能使用此类软件也能使用WSL2，请继续向下看：</p><p>我首先通过搜索找到了这样一篇文章：<a href="https://wangyj.medium.com/the-solution-to-wsl-error-the-attempted-operation-is-not-supported-for-the-type-of-object-aa559854d1e3">https://wangyj.medium.com/the-solution-to-wsl-error-the-attempted-operation-is-not-supported-for-the-type-of-object-aa559854d1e3</a></p><p>我发现它的思路是正确的，但对当前最新版本的WSL2（从Micosoft store安装或更新的）而言不适用，需要做一些小调整。<br>下面分别详细介绍早期版本和当前最新版的详细修复方法。</p><h2 id="早期版本的步骤（如果你是新安装的Win系统或从-“启用或关闭Windows功能”-安装的wsl）："><a href="#早期版本的步骤（如果你是新安装的Win系统或从-“启用或关闭Windows功能”-安装的wsl）：" class="headerlink" title="早期版本的步骤（如果你是新安装的Win系统或从 “启用或关闭Windows功能” 安装的wsl）："></a>早期版本的步骤（如果你是新安装的Win系统或从 “启用或关闭Windows功能” 安装的wsl）：</h2><ol><li>下载 Nolsp.exe：<a href="http://www.proxifier.com/tmp/Test20200228/NoLsp.exe">http://www.proxifier.com/tmp/Test20200228/NoLsp.exe</a></li><li>通过 <code>NoLSP.exe &quot;C:\Windows\System32\wsl.exe&quot;</code>配置 nolsp，它会添加适当的注册表项。</li><li>关闭所有 wsl 终端并重新打开 wsl<br>现在，wsl2 可以正常工作了。</li></ol><p>(替代方法）如果不想使用 Nolsp.exe 二进制文件，可以使用 regedit.exe 在注册表中手动添加以下项目。</p><figure class="highlight plaintext"><table><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">Windows Registry Editor Version 5.00</span><br><span class="line"></span><br><span class="line">[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\AppId_Catalog\0408F7A3]</span><br><span class="line">&quot;AppFullPath&quot;=&quot;C:\Windows\System32\wsl.exe&quot;</span><br><span class="line">&quot;PermittedLspCategories&quot;=dword:80000000</span><br></pre></td></tr></table></figure><p>（或保存为.reg文件运行导入）</p><h2 id="如果你是从-Micosoft-store-安装的WSL，或者你曾经运行过-wsl-update-（也是-Store-版-WSL）："><a href="#如果你是从-Micosoft-store-安装的WSL，或者你曾经运行过-wsl-update-（也是-Store-版-WSL）：" class="headerlink" title="如果你是从 Micosoft store 安装的WSL，或者你曾经运行过 wsl --update （也是 Store 版 WSL）："></a>如果你是从 Micosoft store 安装的WSL，或者你曾经运行过 <code>wsl --update</code> （也是 Store 版 WSL）：</h2><p>这两种情况使用的是 Micosoft Store 中的 wsl2，它们真正执行主要功能的程序路径不同，因此在修复步骤中需要指定 WinApps 应用程序的文件路径（”C:\Program Files\WindowsApps\WSl安装文件夹名\wsl.exe”），而不是系统目录中”C:\Windows\System32\wsl.exe”，另外，最新版本还涉及wslservice.exe的文件（它作为后台服务运行，也需要重启）</p><p>首先，你可以通过在 cmd(admin) 中执行以下指令来查看WSl安装文件夹名：</p><figure class="highlight cmd"><table><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="built_in">cd</span> &quot;C:\Program Files\WindowsApps\&quot;</span><br><span class="line"><span class="built_in">dir</span> MicrosoftCorporationII.WindowsSubsystemForLinux_*</span><br></pre></td></tr></table></figure><p>你会看到WSl安装文件夹名，格式类似 <code>MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe</code>，复制它并且在后续步骤中的路径中替换WSl安装文件夹名，完整的路径类似<code>C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe\wsl.exe</code>。<br>(不同的 wsl2 版本有不同的安装文件夹名。</p><p>步骤：</p><ol><li>下载 Nolsp.exe: <a href="http://www.proxifier.com/tmp/Test20200228/NoLsp.exe">http://www.proxifier.com/tmp/Test20200228/NoLsp.exe</a></li><li>运行 <code>NoLSP.exe &quot;C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe\wsl.exe&quot;</code>, 它会添加一个正确的注册表。</li><li>运行 <code>NoLSP.exe &quot;C:\Program Files\WindowsApps\MicrosoftCorporationII.WindowsSubsystemForLinux_1.2.5.0_x64__8wekyb3d8bbwe\wslservice.exe&quot;</code> ，它将添加一个正确的注册表。<br>(注意：如果你使用的是不同的版本，记得在以上两步使用你的WSl安装文件夹名来替换掉<code>1.2.5.0_x64__8wekyb3d8bbwe</code>） </li><li>运行 <code>wsl --shutdown &amp;&amp; net stop WslService &amp;&amp; net start WslService</code>，关闭所有 wsl 终端并重新打开 wsl<br>现在，wsl2 可以正常工作了。</li></ol><p>另外，请注意，每次 wsl 更新后，WindowsApps 的路径都会发生变化（因此将来更新后需要重新配置 nolsp）。</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/6bb31798.html</id>
    <link href="https://blog.moew.xyz/posts/6bb31798.html"/>
    <published>2023-08-07T09:49:52.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>WSL2 Error code: Wsl/Service/0x8007273d解决</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>有时候我们在配置一些代码编辑器的intellisense功能时，需要添加编译时的系统头文件列表，而这些不是太容易寻找（可能有很多个目录组成），这时候我们可以使用以下方法：</p><hr><p>运行<code>gcc -xc++ -E -v -</code>，<br>该命令通过指定C++语言选项-xc++来启动GCC编译器，并使用-E选项告诉它仅进行预处理，-v选项启用详细输出。最后的-表示从标准输入中读取代码。</p><p>运行上述命令后，GCC将输出许多详细信息，可以在输出中找到类似以下内容的行：</p><figure class="highlight plaintext"><table><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">#include &quot;...&quot; search starts here:</span><br><span class="line">#include &lt;...&gt; search starts here:</span><br><span class="line"> /path/to/include/dir1</span><br><span class="line"> /path/to/include/dir2</span><br><span class="line"> ...</span><br></pre></td></tr></table></figure><p>这些目录路径就是gcc编译器的默认include目录。</p><p>如果不需要c++的头文件而只需要c语言的头文件，也可以将选项-xc++换成-xc。</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/2fa9043f.html</id>
    <link href="https://blog.moew.xyz/posts/2fa9043f.html"/>
    <published>2023-07-01T13:57:25.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>查看gcc编译器的默认include目录</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="算法" scheme="https://blog.moew.xyz/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <category term="LeetCode" scheme="https://blog.moew.xyz/tags/LeetCode/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>LeetCode原题“1017. 负二进制转换”：<a href="https://leetcode.cn/problems/convert-to-base-2/">https://leetcode.cn/problems/convert-to-base-2/</a></p><blockquote><p>给你一个整数 n ，以二进制字符串的形式返回该整数的 负二进制（base -2）表示。</p><p>注意，除非字符串就是 “0”，否则返回的字符串中不能含有前导零。</p></blockquote><figure class="highlight plaintext"><table><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></pre></td><td class="code"><pre><span class="line">示例 1：</span><br><span class="line"></span><br><span class="line">输入：n = 2</span><br><span class="line">输出：&quot;110&quot;</span><br><span class="line">解释：(-2)2 + (-2)1 = 2</span><br><span class="line">示例 2：</span><br><span class="line"></span><br><span class="line">输入：n = 3</span><br><span class="line">输出：&quot;111&quot;</span><br><span class="line">解释：(-2)2 + (-2)1 + (-2)0 = 3</span><br><span class="line">示例 3：</span><br><span class="line"></span><br><span class="line">输入：n = 4</span><br><span class="line">输出：&quot;100&quot;</span><br><span class="line">解释：(-2)2 = 4</span><br></pre></td></tr></table></figure><p>本题给出了负二进制的转换</p><blockquote><p>给你一个整数 n ，以二进制字符串的形式返回该整数的 负二进制（base -2）表示。</p><p>注意，除非字符串就是 “0”，否则返回的字符串中不能含有前导零。</p></blockquote><p>即，<code>a_0*(-2)^0 + a_1*(-2)^1 +... = n</code>，其中ai在负二进制下为0或1，求<code>a_i</code>序列</p><p>答案：</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">string <span class="title">baseNeg2</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span> || n == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">to_string</span>(n);</span><br><span class="line">        &#125;</span><br><span class="line">        string res;</span><br><span class="line">        <span class="keyword">while</span> (n != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> remainder = n &amp; <span class="number">1</span>;</span><br><span class="line">            res.<span class="built_in">push_back</span>(<span class="string">&#x27;0&#x27;</span> + remainder);</span><br><span class="line">            n -= remainder;</span><br><span class="line">            n /= <span class="number">-2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">reverse</span>(res.<span class="built_in">begin</span>(), res.<span class="built_in">end</span>());</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 作者：LeetCode-Solution</span></span><br></pre></td></tr></table></figure><p>那么现在提问，进行扩展：</p><ol><li>如果需要转换为任意负M进制呢？</li><li>如何同时支持正进制和负进制？</li></ol><p>即，<code>a_0*M^0 + a_1*M^1 +... = n</code>，其中ai在负M进制下为0到-N-1，在正M进制下为0到N-1，求<code>a_i</code>序列</p><p>答案：</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">const</span> <span class="type">int</span> M = <span class="number">-2</span>;</span><br><span class="line">    <span class="function">string <span class="title">baseNeg2</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(n==<span class="number">0</span>) <span class="keyword">return</span> <span class="string">&quot;0&quot;</span>s;</span><br><span class="line">        string ans;</span><br><span class="line">        <span class="keyword">while</span>(n) &#123;</span><br><span class="line">            <span class="type">int</span> remainder = n % toM;</span><br><span class="line">            <span class="keyword">if</span>(remainder&lt;<span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// n=M*quotient+remainder</span></span><br><span class="line">                <span class="comment">// 当n为正数而M是负数时，n%M的结果remainder在大多数编程语言（c/c++/java）也是负数，remainder调回正数只需要-M（加上M的绝对值）</span></span><br><span class="line">                <span class="comment">// 同时为了保持n不变，商需要+1（M*quotient为更大的负）来抵消掉多出来的部分</span></span><br><span class="line">                remainder-=M;</span><br><span class="line">                n = n/M<span class="number">+1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 当余数恰好为0 或者 n和M同号（此时remainder为正），不需要任何处理</span></span><br><span class="line">                n /= M;</span><br><span class="line">            &#125;</span><br><span class="line">            ans.<span class="built_in">push_back</span>(<span class="string">&#x27;0&#x27;</span> + remainder);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">reverse</span>(ans.<span class="built_in">begin</span>(),ans.<span class="built_in">end</span>());</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>参考资料：</p><ul><li><a href="https://www.jb51.net/article/222068.htm">C语言中四种取整方式,取余&#x2F;取模运算以及负数取模问题详解</a></li><li><a href="https://blog.csdn.net/qq_40774175/article/details/104062418">2020字节跳动面试算法题—十进制转负3进制</a></li></ul>]]>
    </content>
    <id>https://blog.moew.xyz/posts/1fe65296.html</id>
    <link href="https://blog.moew.xyz/posts/1fe65296.html"/>
    <published>2023-04-10T09:44:30.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>负二进制转换与负M进制转换</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="算法" scheme="https://blog.moew.xyz/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>记录一下：</p><p>x&amp;(-x)：保留二进制下最后出现的1的位置，其余位置置0<br>x&amp;(x-1)：消除二进制下最后出现1的位置，其余保持不变</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/4f72ceb3.html</id>
    <link href="https://blog.moew.xyz/posts/4f72ceb3.html"/>
    <published>2022-10-21T07:20:37.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>两个很经典的位运算记录</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="算法" scheme="https://blog.moew.xyz/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><figure class="highlight cpp"><table><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></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">double</span> PFACTOR = <span class="number">0.25</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> MAX_LEVEL = <span class="number">32</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Skiplist</span> &#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        <span class="type">int</span> val;</span><br><span class="line">        vector&lt;Node*&gt; forward;</span><br><span class="line">        <span class="built_in">Node</span>(<span class="type">int</span> _val, <span class="type">int</span> level): <span class="built_in">val</span>(_val), forward(level, <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    random_device rd;</span><br><span class="line">    mt19937 gen&#123;<span class="built_in">rd</span>()&#125;;</span><br><span class="line">    uniform_real_distribution&lt;<span class="type">double</span>&gt; dis&#123;<span class="number">0</span>, <span class="number">1</span>&#125;;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Node* head;</span><br><span class="line">    <span class="type">int</span> level;</span><br><span class="line">    <span class="built_in">Skiplist</span>() &#123;</span><br><span class="line">        head = <span class="keyword">new</span> <span class="built_in">Node</span>(<span class="number">-1</span>, MAX_LEVEL);</span><br><span class="line">        level = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">search</span><span class="params">(<span class="type">int</span> target)</span> </span>&#123;</span><br><span class="line">        Node *cur=<span class="keyword">this</span>-&gt;head;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=level<span class="number">-1</span>; i&gt;=<span class="number">0</span>; i--) &#123;</span><br><span class="line">            <span class="keyword">while</span>(cur-&gt;forward[i] &amp;&amp; cur-&gt;forward[i]-&gt;val &lt; target) &#123;</span><br><span class="line">                cur = cur-&gt;forward[i];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        cur = cur-&gt;forward[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">return</span> cur &amp;&amp; cur-&gt;val == target;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> num)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> lv=<span class="built_in">randomLv</span>();</span><br><span class="line">        level=<span class="built_in">max</span>(level, lv);</span><br><span class="line">        array&lt;Node*, MAX_LEVEL&gt; shouldupdate;</span><br><span class="line">        Node *cur=<span class="keyword">this</span>-&gt;head;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=level<span class="number">-1</span>; i&gt;=<span class="number">0</span>; i--) &#123; <span class="comment">// 找到各层要更新的结点</span></span><br><span class="line">            <span class="keyword">while</span>(cur-&gt;forward[i] &amp;&amp; cur-&gt;forward[i]-&gt;val &lt; num) &#123;</span><br><span class="line">                cur = cur-&gt;forward[i];</span><br><span class="line">            &#125;</span><br><span class="line">            shouldupdate[i] = cur; <span class="comment">// 虽然全部记录了但是并不是全部都用了</span></span><br><span class="line">        &#125;</span><br><span class="line">        Node *newNode=<span class="keyword">new</span> <span class="built_in">Node</span>(num, lv);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;lv; i++) &#123; <span class="comment">// 只有前Lv层（结点所在那些层级）的前序节点才会被更新</span></span><br><span class="line">            newNode-&gt;forward[i] = shouldupdate[i]-&gt;forward[i];</span><br><span class="line">            shouldupdate[i]-&gt;forward[i] = newNode;</span><br><span class="line">        &#125;</span><br><span class="line">         <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">erase</span><span class="params">(<span class="type">int</span> num)</span> </span>&#123;</span><br><span class="line">        array&lt;Node*, MAX_LEVEL&gt; shouldupdate;</span><br><span class="line">        Node *cur=<span class="keyword">this</span>-&gt;head;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=level<span class="number">-1</span>; i&gt;=<span class="number">0</span>; i--) &#123;</span><br><span class="line">            <span class="keyword">while</span>(cur-&gt;forward[i] &amp;&amp; cur-&gt;forward[i]-&gt;val &lt; num) &#123;</span><br><span class="line">                cur = cur-&gt;forward[i];</span><br><span class="line">            &#125;</span><br><span class="line">            shouldupdate[i] = cur;</span><br><span class="line">        &#125;</span><br><span class="line">        cur = cur-&gt;forward[<span class="number">0</span>]; <span class="comment">// 最底层再推进一次应该就是要删除的元素</span></span><br><span class="line">        <span class="keyword">if</span> (!(cur &amp;&amp; cur-&gt;val == num)) <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 说明当前skiplist不存在该元素</span></span><br><span class="line">        <span class="comment">// 从最底0层开始更新前序节点，直到没有这个元素的层数上</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;level &amp;&amp; shouldupdate[i]-&gt;forward[i]==cur; i++) &#123;</span><br><span class="line">            shouldupdate[i]-&gt;forward[i] = cur-&gt;forward[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">delete</span> cur; <span class="comment">// 删除节点本身</span></span><br><span class="line">        <span class="keyword">while</span>(level&gt;<span class="number">1</span> &amp;&amp; !head-&gt;forward[level<span class="number">-1</span>]) &#123; <span class="comment">// 如果删除节点使得顶上几层变为了空，我们需要删除顶上这些层</span></span><br><span class="line">            level--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">randomLv</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> lv=<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(<span class="built_in">dis</span>(gen)&lt;PFACTOR &amp;&amp; lv&lt;MAX_LEVEL) lv++;</span><br><span class="line">        <span class="keyword">return</span> lv;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Your Skiplist object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * Skiplist* obj = new Skiplist();</span></span><br><span class="line"><span class="comment"> * bool param_1 = obj-&gt;search(target);</span></span><br><span class="line"><span class="comment"> * obj-&gt;add(num);</span></span><br><span class="line"><span class="comment"> * bool param_3 = obj-&gt;erase(num);</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.moew.xyz/posts/2e152a56.html</id>
    <link href="https://blog.moew.xyz/posts/2e152a56.html"/>
    <published>2022-09-22T14:36:51.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>跳表</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="算法" scheme="https://blog.moew.xyz/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>可以原地实现打乱数组。</p><figure class="highlight c++"><table><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">vector&lt;<span class="type">int</span>&gt; nums; <span class="type">int</span> n=nums.<span class="built_in">size</span>();</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; i++) &#123;</span><br><span class="line">    uniform_int_distribution&lt;&gt; <span class="built_in">dis</span>(i, n<span class="number">-1</span>);  <span class="comment">// range is [i, n-1]</span></span><br><span class="line">    <span class="type">int</span> j=<span class="built_in">dis</span>(gen);</span><br><span class="line">    <span class="built_in">swap</span>(nums[i], nums[j]);</span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">return</span> nums;</span><br></pre></td></tr></table></figure><p>该程序循环n次，每次从[i, n-1]选取一个数字，然后把它交换到第i位。</p><p>概率分析：<br>原数组中的任意一个数字，在第i次被选出从而放到第i个位置的概率是</p><figure class="highlight plaintext"><table><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">P(0)=1/n</span><br><span class="line">P(1)=(n-1/n)*(1/n-1)=1/n</span><br><span class="line">...</span><br><span class="line">P(i)= (n-1/n)*(n-2/n-1)*...*(1/n-i)=1/n</span><br></pre></td></tr></table></figure><p>所以每个数字出现在每个位置的概率等同。</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/3453e333.html</id>
    <link href="https://blog.moew.xyz/posts/3453e333.html"/>
    <published>2022-07-26T03:33:54.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>Fisher-Yates 洗牌算法</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="算法" scheme="https://blog.moew.xyz/categories/%E7%AE%97%E6%B3%95/"/>
    <category term="算法" scheme="https://blog.moew.xyz/tags/%E7%AE%97%E6%B3%95/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>维基百科中的步骤：</p><blockquote><ol><li>Find the largest index k such that a[k] &lt; a[k + 1]. If no such index exists, the permutation is the last permutation.</li><li>Find the largest index l greater than k such that a[k] &lt; a[l].</li><li>Swap the value of a[k] with that of a[l].</li><li>Reverse the sequence from a[k + 1] up to and including the final element a[n].</li></ol></blockquote><p>至于为什么这么做就可以得到下一个排列，可以看代码及注释。</p><p>实现代码：</p><figure class="highlight c++"><table><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="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">nextPermutation</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.<span class="built_in">size</span>()==<span class="number">1</span>) <span class="keyword">return</span>;</span><br><span class="line">        <span class="comment">// find the largest index i which statisfies nums[i]&lt;nums[i+1]</span></span><br><span class="line">        <span class="comment">// 从后往前找到交换点i，交换点i以后的数全部都是降序的</span></span><br><span class="line">        <span class="comment">// 因为i后面的数[i+1, n)已经无法变得更大，所以我们不得不让i位置变大一点来得到下一个序列</span></span><br><span class="line">        <span class="type">int</span> i=nums.<span class="built_in">size</span>()<span class="number">-2</span>;</span><br><span class="line">        <span class="keyword">while</span>(i&gt;=<span class="number">0</span> &amp;&amp; nums[i]&gt;=nums[i<span class="number">+1</span>]) &#123;</span><br><span class="line">            i--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(i&lt;<span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果找不到这么一个位置i，表示整个序列都是降序的，即已经是最后一个排列。</span></span><br><span class="line">            <span class="comment">// 重新得到第一个排列的方法是整个数组逆序。</span></span><br><span class="line">            <span class="built_in">reverse</span>(nums.<span class="built_in">begin</span>(), nums.<span class="built_in">end</span>());</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 可想而知，为了是变大程度尽量小，i前面的数不应该改动，我们从i后面的数里面挑出一个比i稍大的</span></span><br><span class="line">        <span class="comment">// 因为后面是降序，所以可以直接按位置查找</span></span><br><span class="line">        <span class="comment">// find the largest index j which statisfies nums[j]&gt;nums[j]</span></span><br><span class="line">        <span class="type">int</span> j=nums.<span class="built_in">size</span>()<span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">while</span>(nums[j] &lt;= nums[i]) &#123;</span><br><span class="line">            j--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 我们将这个数nums[j]交换到nums[i]的位置</span></span><br><span class="line">        <span class="built_in">swap</span>(nums[i], nums[j]); </span><br><span class="line">        <span class="comment">// 因为我们找到的nums[j]是比nums[i]刚刚稍小的数，所以交换完成后，后面的区间[i+1,n]仍然是降序的</span></span><br><span class="line">        <span class="comment">// 此时还没有结束。因为后面的区间[i+1,n]是降序的，我们知道降序是最大的排列（最后一个排列）而不是最小的排序</span></span><br><span class="line">        <span class="comment">// 我们将这个降序区间调整回为升序区间，此时才是下一个排列</span></span><br><span class="line">        <span class="built_in">reverse</span>(nums.<span class="built_in">begin</span>()+i<span class="number">+1</span>, nums.<span class="built_in">end</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>相关练习题：<a href="https://leetcode.cn/problems/next-permutation">https://leetcode.cn/problems/next-permutation</a></p><p>参考资料：<br><a href="https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-leetcode-solution/">https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-leetcode-solution/</a><br><a href="https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-powcai/">https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-by-powcai/</a><br><a href="https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order">https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order</a></p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/511976fb.html</id>
    <link href="https://blog.moew.xyz/posts/511976fb.html"/>
    <published>2022-07-03T10:23:59.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>全排列中获取下一个排列的实现（c++ STL next_permutation）</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
  <entry>
    <author>
      <name>听寒</name>
    </author>
    <category term="使用经验" scheme="https://blog.moew.xyz/categories/%E4%BD%BF%E7%94%A8%E7%BB%8F%E9%AA%8C/"/>
    <category term="使用经验" scheme="https://blog.moew.xyz/tags/%E4%BD%BF%E7%94%A8%E7%BB%8F%E9%AA%8C/"/>
    <content>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p>wsl1中主机和wsl共享网络，但在wsl2中是单独的网络。<br>可以借助如下命令配置代理（可写在~&#x2F;.profile里）</p><figure class="highlight bash"><table><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">host_ip=$(<span class="built_in">cat</span> /etc/resolv.conf |grep <span class="string">&quot;nameserver&quot;</span> |<span class="built_in">cut</span> -f 2 -d <span class="string">&quot; &quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># system proxy</span></span><br><span class="line"><span class="built_in">export</span> ALL_PROXY=<span class="string">&quot;http://<span class="variable">$host_ip</span>:10809&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># go proxy</span></span><br><span class="line">GOPROXY=<span class="string">&quot;https://goproxy.cn,direct&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># git proxy</span></span><br><span class="line">git config --global http.proxy socks5://<span class="variable">$host_ip</span>:10808</span><br><span class="line">git config --global https.proxy socks5://<span class="variable">$host_ip</span>:10808</span><br></pre></td></tr></table></figure><p>记得打开代理软件的接受来自局域网的连接</p>]]>
    </content>
    <id>https://blog.moew.xyz/posts/b062efae.html</id>
    <link href="https://blog.moew.xyz/posts/b062efae.html"/>
    <published>2022-05-17T02:43:02.000Z</published>
    <summary>
      <![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" cla]]>
    </summary>
    <title>wsl2配置代理</title>
    <updated>2026-06-25T01:53:52.126Z</updated>
  </entry>
</feed>
