<?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/"><channel><title>Io on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/io/</link><description>Recent content in Io on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 02 Aug 2026 09:10:28 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/io/index.xml" rel="self" type="application/rss+xml"/><item><title>Blocking vs Non-blocking I/O: How a Thread Waits for Data</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-blocking-vs-non-blocking-i-o-how-a-thread-waits-for-data/</link><pubDate>Sun, 02 Aug 2026 09:10:28 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-blocking-vs-non-blocking-i-o-how-a-thread-waits-for-data/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Blocking and non-blocking I/O differ in what happens to the calling thread when a read or write can&amp;rsquo;t complete immediately. Blocking I/O suspends the thread until data is ready; non-blocking I/O returns at once and lets the caller check back later. The choice shapes how a system scales to many simultaneous connections.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="45" x2="320" y2="315" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;text x="160" y="28" text-anchor="middle" font-size="16" font-weight="600" style="fill:var(--primary)"&gt;Blocking I/O&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" font-size="16" font-weight="600" style="fill:var(--primary)"&gt;Non-blocking I/O&lt;/text&gt;&lt;rect x="100" y="50" width="120" height="30" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="70" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Thread&lt;/text&gt;&lt;line x1="160" y1="80" x2="160" y2="102" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="160,105 156,98 164,98" style="fill:var(--secondary)"/&gt;&lt;rect x="70" y="105" width="180" height="30" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="125" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;call read()&lt;/text&gt;&lt;line x1="160" y1="135" x2="160" y2="157" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="160,160 156,153 164,153" style="fill:var(--secondary)"/&gt;&lt;rect x="70" y="160" width="180" height="85" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="5,3"/&gt;&lt;text x="160" y="198" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;thread blocked&lt;/text&gt;&lt;text x="160" y="216" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;no other work possible&lt;/text&gt;&lt;text x="160" y="234" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;until data arrives&lt;/text&gt;&lt;line x1="160" y1="245" x2="160" y2="267" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="160,270 156,263 164,263" style="fill:var(--secondary)"/&gt;&lt;rect x="70" y="270" width="180" height="30" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="290" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;data ready, resumes&lt;/text&gt;&lt;text x="160" y="330" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;1 thread ≈ 1 in-flight call&lt;/text&gt;&lt;rect x="390" y="50" width="180" height="30" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="70" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;thread / event loop&lt;/text&gt;&lt;line x1="480" y1="80" x2="480" y2="102" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="480,105 476,98 484,98" style="fill:var(--secondary)"/&gt;&lt;rect x="390" y="105" width="180" height="30" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="125" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;read() returns at once&lt;/text&gt;&lt;line x1="480" y1="135" x2="480" y2="157" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="480,160 476,153 484,153" style="fill:var(--secondary)"/&gt;&lt;rect x="390" y="160" width="180" height="35" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="182" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;poll / epoll check&lt;/text&gt;&lt;path d="M 570 168 C 595 168 595 190 572 190" style="stroke:var(--compare-b);fill:none" stroke-width="1.3"/&gt;&lt;polygon points="572,190 579,187 578,194" style="fill:var(--compare-b)"/&gt;&lt;text x="596" y="182" font-size="9" style="fill:var(--secondary)"&gt;retry&lt;/text&gt;&lt;line x1="480" y1="195" x2="480" y2="207" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="480,210 476,203 484,203" style="fill:var(--secondary)"/&gt;&lt;rect x="390" y="210" width="180" height="35" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="228" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;meanwhile: serves other&lt;/text&gt;&lt;text x="480" y="241" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;connections&lt;/text&gt;&lt;line x1="480" y1="245" x2="480" y2="267" style="stroke:var(--secondary)" stroke-width="1.5"/&gt;&lt;polygon points="480,270 476,263 484,263" style="fill:var(--secondary)"/&gt;&lt;rect x="390" y="270" width="180" height="30" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="290" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;ready → callback fires&lt;/text&gt;&lt;text x="480" y="330" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;1 thread ≈ many in-flight calls&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Blocking I/O&lt;/th&gt;
&lt;th&gt;Non-blocking I/O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Call behavior&lt;/td&gt;
&lt;td&gt;Call halts the calling thread until the operation completes&lt;/td&gt;
&lt;td&gt;Call returns immediately, with data or an EWOULDBLOCK/EAGAIN error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread state while I/O is pending&lt;/td&gt;
&lt;td&gt;Thread is suspended off the run queue — no CPU used, but unavailable for other work&lt;/td&gt;
&lt;td&gt;Thread stays runnable and can be reused to serve other requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How readiness is discovered&lt;/td&gt;
&lt;td&gt;OS wakes the thread automatically once data is ready&lt;/td&gt;
&lt;td&gt;Caller polls or registers with select/poll/epoll/kqueue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency model&lt;/td&gt;
&lt;td&gt;One thread (or process) per concurrent connection&lt;/td&gt;
&lt;td&gt;Single or few threads multiplex many connections via an event loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource overhead at scale&lt;/td&gt;
&lt;td&gt;Grows linearly with connections — thread stacks, context switches&lt;/td&gt;
&lt;td&gt;Stays flat, bounded by CPU cores rather than connection count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code / control-flow complexity&lt;/td&gt;
&lt;td&gt;Simple, sequential, top-to-bottom logic&lt;/td&gt;
&lt;td&gt;Callback, promise, or async/await structure; state tracked across suspensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure / edge-case handling&lt;/td&gt;
&lt;td&gt;A slow or hung peer blocks the thread indefinitely without a timeout&lt;/td&gt;
&lt;td&gt;A slow peer only delays its own event; a stalled callback can starve the whole loop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Blocking I/O ties up a &lt;strong class="kw"&gt;thread&lt;/strong&gt; for the full call; non-blocking frees it immediately.&lt;/li&gt;
&lt;li&gt;Non-blocking servers rely on an &lt;strong class="kw"&gt;event loop&lt;/strong&gt; to learn when data is finally ready.&lt;/li&gt;
&lt;li&gt;Blocking scales concurrency with more &lt;strong class="kw"&gt;threads&lt;/strong&gt;; non-blocking scales with more &lt;strong class="kw"&gt;callbacks&lt;/strong&gt; on fewer threads.&lt;/li&gt;
&lt;li&gt;Blocking code reads &lt;strong class="kw"&gt;sequentially&lt;/strong&gt;; non-blocking code needs explicit &lt;strong class="kw"&gt;state management&lt;/strong&gt; across suspensions.&lt;/li&gt;
&lt;li&gt;At high connection counts, blocking hits a &lt;strong class="kw"&gt;C10K&lt;/strong&gt; wall that non-blocking avoids.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Blocking I/O&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>