<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Multithreading｜林鼎淵 Dean Lin</title>
    <link>https://deanlin.net/tags/multithreading/</link>
    <image><url>https://deanlin.net/images/author_hu_aa256fc934e9a932.jpg</url><title>Multithreading｜林鼎淵 Dean Lin</title><link>https://deanlin.net/tags/multithreading/</link><width>144</width><height>144</height></image>
    <description>林鼎淵（Dean Lin）的部落格：生成式 AI 企業內訓講師、外商資安公司軟體專家、7 本書作者。400 多篇文章用白話分享生成式 AI 實戰、Claude Code 與 Vibe Coding 教學、團隊合作與職場成長。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>© 2020–2026 林鼎淵 Dean Lin</copyright>
    <lastBuildDate>Thu, 20 Oct 2022 12:06:17 +0800</lastBuildDate>
    <atom:link href="https://deanlin.net/tags/multithreading/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Node.js &#43; worker_threads 多執行緒實戰範例</title>
      <link>https://deanlin.net/blog/202210/nodejs-worker-threads/</link>
      <pubDate>Thu, 20 Oct 2022 12:06:17 +0800</pubDate>
      <guid>https://deanlin.net/blog/202210/nodejs-worker-threads/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>技術教學</category>
      <description>一般狀況下 Node.js 為單執行緒，如果同步執行時遇到「阻塞」的程式，後續要執行的程式就會全部被卡住；為了解決這個問題， Node.js 在 v12 推出了 worker_threads，這個 Module 讓 Node.js 可以做多執行緒的平行處理！</description>
      <media:content url="https://deanlin.net/blog/202210/nodejs-worker-threads/images/nodejs-worker-threads-og_hu_615bee066aaa9969.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>一般狀況下 Node.js 為單執行緒，如果同步執行時遇到「阻塞」的程式，後續要執行的程式就會全部被卡住；為了解決這個問題， Node.js 在 v12 推出了 <code>worker_threads</code>，這個 Module 讓 Node.js 可以做多執行緒的平行處理！</p>
<p><img src="https://deanlin.net/blog/202210/nodejs-worker-threads/images/nodejs-worker-threads-01.webp" width="1400" height="732" alt="Node.js &#43; worker_threads 多執行緒實戰範例（圖 1）" fetchpriority="high" decoding="async"></p>
<blockquote>
<p>今天這篇文章會採用 Demo 的形式，讓大家理解程式運作的順序，而「阻塞」的程式則是選用 <code>斐波那契數列（fibonacci sequence）</code> 來模擬。</p>
</blockquote>
<h2 id="單執行緒會遇到什麼問題">單執行緒會遇到什麼問題<a class="h-anchor" href="#%e5%96%ae%e5%9f%b7%e8%a1%8c%e7%b7%92%e6%9c%83%e9%81%87%e5%88%b0%e4%bb%80%e9%ba%bc%e5%95%8f%e9%a1%8c" aria-label="這個段落的連結"></a></h2>
<p>單執行緒如果遇上需要高 CPU 運算的程式，那整體效能就會大幅下降；下面我們用一個簡單的程式來做說明：</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">function</span> getFibonacciNumber (num) {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">if</span> (num <span style="color:#ff7b72;font-weight:bold">===</span> <span style="color:#a5d6ff">0</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">0</span>;
</span></span><span style="display:flex;"><span>  } <span style="color:#ff7b72">else</span> <span style="color:#ff7b72">if</span> (num <span style="color:#ff7b72;font-weight:bold">===</span> <span style="color:#a5d6ff">1</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">1</span>;
</span></span><span style="display:flex;"><span>  } <span style="color:#ff7b72">else</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> getFibonacciNumber(num <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span>) <span style="color:#ff7b72;font-weight:bold">+</span> getFibonacciNumber(num <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">2</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">`42th Fibonacci Result:`</span> <span style="color:#ff7b72;font-weight:bold">+</span> getFibonacciNumber(<span style="color:#a5d6ff">42</span>));
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">`15th Fibonacci Result:`</span> <span style="color:#ff7b72;font-weight:bold">+</span> getFibonacciNumber(<span style="color:#a5d6ff">15</span>));
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">&#34;被卡住的輕量級任務&#34;</span>);
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/ef1bc467983a03bb631a90dcdd5a8d54" rel="noopener">single-thread.js</a>（GitHub Gist）</p>
<p>上述程式的執行結果為：</p>
<pre tabindex="0"><code>42th Fibonacci Result:267914296
15th Fibonacci Result:610
被卡住的輕量級任務
</code></pre><p>你可以將程式中的 3 個 console.log 理解為：</p>
<ol>
<li><strong>42th Fibonacci</strong>：會讓程式阻塞的重量級運算。</li>
<li><strong>15th Fibonacci</strong>：運算需要的時間不長，但被上面的程式卡住了。</li>
<li><strong>被卡住的輕量級任務</strong>：只是簡單的輸出，卻被上面的程式卡的動彈不得。</li>
</ol>
<hr>
<h2 id="多執行緒如何解決">多執行緒如何解決<a class="h-anchor" href="#%e5%a4%9a%e5%9f%b7%e8%a1%8c%e7%b7%92%e5%a6%82%e4%bd%95%e8%a7%a3%e6%b1%ba" aria-label="這個段落的連結"></a></h2>
<p>既然知道 <code>斐波那契數列（fibonacci sequence）</code> 是阻塞的元兇，那我們可以把這個運算抽離出來，給新的執行緒來計算。</p>
<p>在實作方面，先新增一個 <code>worker.js</code> 的檔案，讓他處理由「主執行緒」指派的「新執行緒」；這裡的執行緒會透過 <code>parentPort.postMessage</code> 函數與「主執行緒」傳遞訊息。</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> { parentPort, workerData } <span style="color:#ff7b72;font-weight:bold">=</span> require(<span style="color:#a5d6ff">&#34;worker_threads&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>parentPort.postMessage(getFibonacciNumber(workerData.num))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">function</span> getFibonacciNumber (num) {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">if</span> (num <span style="color:#ff7b72;font-weight:bold">===</span> <span style="color:#a5d6ff">0</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">0</span>;
</span></span><span style="display:flex;"><span>  } <span style="color:#ff7b72">else</span> <span style="color:#ff7b72">if</span> (num <span style="color:#ff7b72;font-weight:bold">===</span> <span style="color:#a5d6ff">1</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">1</span>;
</span></span><span style="display:flex;"><span>  } <span style="color:#ff7b72">else</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> getFibonacciNumber(num <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span>) <span style="color:#ff7b72;font-weight:bold">+</span> getFibonacciNumber(num <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">2</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/15577f1cfec9a82f3c25344f7acaa3df" rel="noopener">worker.js</a>（GitHub Gist）</p>
<p>接著我們新增一個 <code>index.js</code> 的檔案，來設計主執行緒的程式，筆者在這新增一個「runWorker」的函式，將會造成「阻塞」的程式交給它來執行。</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> { Worker } <span style="color:#ff7b72;font-weight:bold">=</span> require(<span style="color:#a5d6ff">&#34;worker_threads&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> runWorker <span style="color:#ff7b72;font-weight:bold">=</span> (number) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">const</span> worker <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">new</span> Worker(<span style="color:#a5d6ff">&#34;./worker.js&#34;</span>, { workerData<span style="color:#ff7b72;font-weight:bold">:</span> { num<span style="color:#ff7b72;font-weight:bold">:</span> number } });
</span></span><span style="display:flex;"><span>  worker.on(<span style="color:#a5d6ff">&#34;message&#34;</span>, result =&gt; {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">`</span><span style="color:#a5d6ff">${</span>number<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">th Fibonacci Result: </span><span style="color:#a5d6ff">${</span>result<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">`</span>);
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  worker.on(<span style="color:#a5d6ff">&#34;error&#34;</span>, error =&gt; {
</span></span><span style="display:flex;"><span>    console.log(error);
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  worker.on(<span style="color:#a5d6ff">&#34;exit&#34;</span>, exitCode =&gt; {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a5d6ff">`It exited with code </span><span style="color:#a5d6ff">${</span>exitCode<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">`</span>);
</span></span><span style="display:flex;"><span>  })
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>runWorker(<span style="color:#a5d6ff">42</span>)
</span></span><span style="display:flex;"><span>runWorker(<span style="color:#a5d6ff">15</span>)
</span></span><span style="display:flex;"><span>console.log(<span style="color:#a5d6ff">&#34;主執行緒的任務&#34;</span>);
</span></span></code></pre></div><p class="code-source">原始碼：<a href="https://gist.github.com/dean9703111/00e22524c68601f108dbe1630f7b1534" rel="noopener">worker-threads.js</a>（GitHub Gist）</p>
<p>經過這樣的調整後：</p>
<ol>
<li>繁重的運算由「新執行緒」處理，且不會互相阻塞；解決了 Fibonacci 在「15th」的運算明明很快，但於單執行緒卻被「42th」拖到後面才執行的問題。</li>
<li>從下圖的運算結果，你可以觀察到主執行緒不會被阻塞。</li>
</ol>
<p><img src="https://deanlin.net/blog/202210/nodejs-worker-threads/images/nodejs-worker-threads-02.webp" width="910" height="278" alt="多執行緒如何解決（圖 2）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="結語">結語<a class="h-anchor" href="#%e7%b5%90%e8%aa%9e" aria-label="這個段落的連結"></a></h2>
<p>過去 Node.js 常常被說不適合執行高 CPU 運算的任務（因為會阻塞）。</p>
<p>但在導入 <code>worker_threads</code> 使其可以運用多執行緒後，我們只要將「高 CPU 運算」的任務交給其他執行緒處理，主執行緒就能繼續處理其他任務不被阻塞，以此提高 Server 的吞吐量。</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
