<?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/">
  <channel>
    <title>Concurrency on KbWen Blog</title>
    <link>https://www.kbwen.com/tags/concurrency/</link>
    <description>KbWen is a practical technology blog about AI systems, machine learning, Python, data engineering, and software development.</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <image>
      <url>https://www.kbwen.com/images/og-default.png</url>
      <title>KbWen Blog</title>
      <link>https://www.kbwen.com/</link>
    </image>
    
    <lastBuildDate>Thu, 10 Sep 2026 15:30:00 +0800</lastBuildDate><atom:link href="https://www.kbwen.com/tags/concurrency/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Python 3.14&#39;s free-threaded build</title>
      <link>https://www.kbwen.com/python-3-14-free-threaded-build/</link>
      <pubDate>Thu, 10 Sep 2026 15:30:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/python-3-14-free-threaded-build/</guid>
      <description>A look at Python 3.14&amp;#39;s free-threaded build through the CPython 3.14 docs and source: installing and identifying it, what happens when it imports a C extension, and what it costs in speed and memory.</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR:</strong> Python 3.14&rsquo;s free-threaded build is a separate interpreter, <code>python3.14t</code>, installed as an optional component. To see whether a running interpreter has the GIL off, call <code>sys._is_gil_enabled()</code> after your imports.</p>
</blockquote>
<p>Python 3.14 came out in October 2025, and <a href="https://docs.python.org/3.14/whatsnew/3.14.html">What&rsquo;s New in Python 3.14</a> says its free-threaded build is &ldquo;now supported and no longer experimental,&rdquo; though still optional. The regular build remains the default, and whether that will change is still undecided. On the free-threaded build, several threads can run Python bytecode at the same time within one interpreter, while the regular build has the global interpreter lock, the GIL, which lets only one thread execute bytecode at a time.</p>
<p>The free-threaded build is its own interpreter, CPython compiled with the <code>--disable-gil</code> configure option, and it installs next to the regular one. On Windows, the Python install manager adds it with <code>py install 3.14t</code>, and the macOS installer from python.org has it as a separate option under Customize, not installed by default. With <a href="/uv-replaces-pip-venv-pyenv/">uv</a>, <code>uv venv --python 3.14t</code> creates an environment on it. The free-threaded executable always has a <code>python3.14t</code> alias, but whether <code>python</code> or <code>python3.14</code> also points at it depends on the install method.</p>
<h2 id="importing-a-c-extension">Importing a C extension</h2>
<p>Running <code>python3.14t -VV</code> prints a version string with &ldquo;free-threading build&rdquo; right after the version number. In 3.13 the same string said &ldquo;experimental free-threading build&rdquo;. The string says what was compiled, but not whether the running interpreter has the GIL off. That has its own check, <code>sys._is_gil_enabled()</code>, which should return <code>False</code> on the free-threaded build before any packages are imported. After startup, importing a C extension can change that answer to <code>True</code>. When that happens CPython prints a <code>RuntimeWarning</code> that names the module:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">The global interpreter lock (GIL) has been enabled to load module &#39;&lt;name&gt;&#39;, which has not declared that it can run safely without the GIL. To override this behavior and keep the GIL disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.
</span></span></code></pre></div><p>&ldquo;Has been enabled&rdquo; reports something already done. Until this import the interpreter had been running without the GIL, through every pure-Python module it loaded and every extension that declared it could run without one. In <a href="https://github.com/python/cpython/blob/3.14/Python/import.c">CPython&rsquo;s import code</a>, a C extension import goes through the same steps each time while the GIL is off: CPython turns the GIL on for the load, stopping the other threads while it does, runs the extension&rsquo;s init function, and then checks what the module declared. If the module declared that it doesn&rsquo;t need the GIL, CPython turns the GIL back off. If it didn&rsquo;t, the GIL stays on, and this time CPython marks it as permanent. From then on, calls to turn the GIL on or off do nothing until the interpreter shuts down. CPython prints the warning only when it makes the GIL permanent, so the module it names is the first undeclared extension the interpreter imported. Later undeclared extensions load without a warning of their own.</p>
<p>The declaration the warning mentions is a slot, <code>Py_mod_gil</code>, in the extension&rsquo;s module definition. An extension that supports running without the GIL adds one line, <code>{Py_mod_gil, Py_MOD_GIL_NOT_USED}</code> (single-phase modules declare it with <code>PyUnstable_Module_SetGIL()</code> instead). When that line is missing, &ldquo;<a href="https://docs.python.org/3.14/c-api/module.html">the import machinery defaults to Py_MOD_GIL_USED</a>.&rdquo; <code>Py_MOD_GIL_USED</code> is the value for a module that &ldquo;may access global state without synchronization.&rdquo; Extension code that predates the slot doesn&rsquo;t have the line, and neither does an extension that happens to be thread-safe but was never marked. Because CPython looks only at the declaration, it treats the two alike. The <a href="https://py-free-threading.github.io/faq/">py-free-threading FAQ</a> traces that default to &ldquo;the long history of extensions assuming the GIL locks concurrent access to extension internals.&rdquo;</p>
<p><code>PYTHON_GIL=0</code> and <code>-Xgil=0</code>, the two settings named in the warning, keep the GIL off and skip the check at import. If both are set, the command-line option takes precedence. <a href="https://peps.python.org/pep-0703/">PEP 703</a>, the proposal the build comes from, gives the reason the override exists: extensions that aren&rsquo;t thread-safe &ldquo;can still be useful in multi-threaded applications&rdquo; when they are used from a single thread or behind a lock.</p>
<p>C extensions need to be <a href="https://docs.python.org/3.14/howto/free-threading-extensions.html">built specifically for the free-threaded build</a>. Those builds carry a <code>t</code> suffix, so a wheel for this interpreter has the tag <code>cp314t</code>. The free-threaded build doesn&rsquo;t currently support the stable ABI, which means a project that ships one <code>abi3</code> wheel for several regular Python versions still needs a separate wheel for 3.14t. Building that wheel and adding the <code>Py_mod_gil</code> line are separate steps, though. A package can publish a <code>cp314t</code> wheel whose module never declares the slot, and that wheel installs on 3.14t normally but can still turn the GIL on when it is imported. The <a href="https://py-free-threading.github.io/tracking/">py-free-threading tracking page</a> and <a href="https://hugovk.github.io/free-threaded-wheels/">Free-threaded wheels</a> both track which popular packages support free threading, and the Free-threaded wheels site marks a package dark green when it offers wheels with an ABI tag ending in <code>t</code>.</p>
<h2 id="speed-and-memory">Speed and memory</h2>
<p>What&rsquo;s New puts the penalty on single-threaded code at &ldquo;roughly 5-10%, depending on the platform and C compiler used.&rdquo; The <a href="https://docs.python.org/3.14/howto/free-threading-python.html">free-threading HOWTO</a> gives the average overhead on the pyperformance benchmark suite as ranging &ldquo;from about 1% on macOS aarch64 to 8% on x86-64 Linux systems.&rdquo;</p>
<p>The free-threaded build also typically uses more memory than the regular build. PEP 779, written in March 2025 before 3.14 was released, put the increase at &ldquo;about 15-20%&rdquo; as a geometric mean over pyperformance.</p>
<p>The HOWTO says that while not all software will benefit automatically, &ldquo;programs designed with threading in mind will run faster on multi-core hardware.&rdquo; Whether that outweighs the overhead comes down to your own program, timed on the regular build and on <code>python3.14t</code>, with <code>sys._is_gil_enabled()</code> checked at the end of the run so that the free-threaded numbers come from an interpreter that had the GIL off.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Python 3.14 的 free-threading build</title>
      <link>https://www.kbwen.com/python-free-threading-no-gil/</link>
      <pubDate>Thu, 10 Sep 2026 09:15:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/python-free-threading-no-gil/</guid>
      <description>Python 3.14 起 free-threading build 正式支援，GIL 可以在執行期關掉。這篇說明 GIL 原本負責什麼、怎麼判斷使用的 python 有沒有真的關掉 GIL，以及關掉之後單執行緒與 C 擴充套件相容性的影響。</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong>：想確認 python 有沒有真的關掉 GIL，跑 <code>python -VV</code> 看是不是 free-threading build，import 完所有套件之後跑 <code>sys._is_gil_enabled()</code>（3.13 起才有），輸出是 <code>False</code> 才代表這次執行真的沒有 GIL。3.14 起 free-threading 正式支援、但目前還不是預設項目；關掉 GIL 的影響是單執行緒變慢，以及沒支援的 C 擴充套件一被匯入就會把 GIL 重新開起來。</p>
</blockquote>
<p>Python 3.14 起，free-threading build 從實驗性變成正式支援。這類的 build 可以在不啟用 GIL 的情況下跑 Python 程式，執行檔通常叫 <code>python3.14t</code>，跟一般的 <code>python3.14</code> 並存。GIL 拿掉之後，多執行緒才可能真的同時執行 Python；但拿掉 GIL，也會影響其他地方。</p>
<h2 id="gil">GIL</h2>
<p>GIL 是 CPython 的全域直譯器鎖，作用是不讓多個執行緒同時執行 Python 程式（PEP 703 說明 prevents multiple threads from executing Python code at the same time）。它的核心理由跟記憶體管理相關。CPython 靠參考計數回收物件，每當有變數指到或不再指到某個物件，那個物件的計數就要加一或減一；而在有 GIL 的前提下，同一時間只有一個執行緒在跑。</p>
<p>要拿掉 GIL，這套邏輯作法就會不一樣。PEP 703 提的做法之一，是把原本非原子的參考計數換成 biased reference counting 這種執行緒安全的計數方式（a switch from plain non-atomic reference counting to biased reference counting）。實作細節這邊不討論，重點的差別是 GIL 換來的其實是「參考計數不必加鎖」；如果拿掉，就得在計數機制上把執行緒安全補回來。</p>
<h2 id="314起正式支援">3.14起正式支援</h2>
<p>3.13 一開始加入 free-threading 時還是實驗性的。到 3.14 就變成正式性質，&ldquo;The free-threaded build of Python is now supported and no longer experimental&rdquo;。對應的規範是 PEP 779，它替「轉到正式支援」這個階段訂定出明確的條件（requirements for moving to Phase II, making the free-threaded Python build officially supported）。</p>
<p>但是正式支援不等於預設。整件事分成幾個階段：Phase II 讓 free-threaded build 變成官方支援、但仍然是選用的，要到 Phase III 才會讓它成為預設（officially supported but still optional, and phase III would make the free-threaded build the default）。以目前的資料看，裝好標準的 python3.14，預設仍然是 GIL 的版本，要 free-threading 得另外取得 free-threading build。</p>
<h2 id="判斷狀態build執行期">判斷狀態：build、執行期</h2>
<p>這是最容易搞混的地方，因為要分兩個地方看。</p>
<p>第一，是安裝的 python 本身支不支援 free-threading。直接跑 <code>python -VV</code>，輸出的版本資訊裡如果寫著 free-threading build，就是支援的 build。程式內也可以查詢編譯設定：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sysconfig</span>
</span></span><span class="line"><span class="cl"><span class="n">sysconfig</span><span class="o">.</span><span class="n">get_config_var</span><span class="p">(</span><span class="s2">&#34;Py_GIL_DISABLED&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>回 <code>1</code> 代表這個 build 支援 free threading（then the build supports free threading），回 <code>0</code> 或 <code>None</code> 就是一般的帶 GIL build。</p>
<p>第二，是這次執行到底有沒有真的把 GIL 關掉。3.13 起有個函式可以查詢：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="n">sys</span><span class="o">.</span><span class="n">_is_gil_enabled</span><span class="p">()</span>
</span></span></code></pre></div><p>如果回 <code>False</code> 才代表這次跑起來 GIL 是關的（HOWTO：can be used to check whether the GIL is actually disabled in the running process）。那你可能會想支援的 build 為什麼還開著 GIL？有兩種情況。一種是明確要求：設環境變數 <code>PYTHON_GIL=1</code>，或啟動時加 <code>-X gil=1</code>，都會把 GIL 開回來（反過來 <code>PYTHON_GIL=0</code> 或 <code>-X gil=0</code> 是關）。另一種比較隱性、也比較常遇到：匯入沒有標記支援 free-threading 的 C 擴充套件時，直譯器會自動把 GIL 重新開起來。</p>
<p>隱性最值得被記住，因為它會讓兩個地方對不起來：build 明明支援，<code>sys._is_gil_enabled()</code> 卻回 True。所以要確認就要把查詢寫在 import 完所有套件之後跑，而不是一開機、還沒載入依賴就執行。</p>
<h2 id="關掉之後">關掉之後</h2>
<p>效能上，關掉 GIL 會讓單執行緒的程式變慢，因為參考計數改成執行緒安全的版本、以及其他為了無鎖並行做的調整。這裡有兩組官方數字，What&rsquo;s New 3.14：單執行緒的效能損失目前大約落在 5 到 10%，會隨平台和 C 編譯器而變（roughly 5-10%）。HOWTO 則是 pyperformance 的實測範圍，平均開銷從 macOS aarch64 上約 1%，到 x86-64 Linux 上約 8%（the average overhead ranges from about 1% on macOS aarch64 to 8% on x86-64 Linux systems）。大家真的想知道可以實際量測差別，以及自己開發產品的類型來決策。</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
