<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Netpoller on Zampo Blog</title><link>https://blog.cpdd.fyi/tags/netpoller/</link><description>Recent content in Netpoller on Zampo Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Fri, 29 May 2026 20:53:18 +0800</lastBuildDate><atom:link href="https://blog.cpdd.fyi/tags/netpoller/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 调度器真正厉害的地方：线程卡住了，P 不能跟着陪葬</title><link>https://blog.cpdd.fyi/posts/go-gmp-syscall-netpoller/</link><pubDate>Fri, 29 May 2026 20:53:18 +0800</pubDate><guid>https://blog.cpdd.fyi/posts/go-gmp-syscall-netpoller/</guid><description>&lt;p&gt;把 &lt;code&gt;GOMAXPROCS&lt;/code&gt; 设成 1，再让一个 goroutine 卡在 &lt;code&gt;syscall.Read&lt;/code&gt; 里。&lt;/p&gt;
&lt;p&gt;按直觉，整个 Go 程序应该只剩一个执行名额。这个名额被卡住了，其他 goroutine 也该一起停。&lt;/p&gt;
&lt;p&gt;但你真跑一下，会看到另一件事：reader 卡在系统调用里，主 goroutine 的 ticker 还在继续打印。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; assets/go-gmp-series/02-syscall-netpoller/examples/syscall-handoff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;GOMAXPROCS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="nv"&gt;GODEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;schedtrace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500,scheddetail&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; go run main.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;示例里一个 goroutine 进入阻塞的 &lt;code&gt;syscall.Read&lt;/code&gt;，3 秒后才有人往 pipe 写数据。与此同时，主 goroutine 每 200ms 仍然输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;main: still running Go code while another M may be blocked in syscall
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这件事比“goroutine 很轻”更关键。&lt;/p&gt;
&lt;p&gt;它说明 Go 调度器真正厉害的地方，不是能创建很多 G，而是能在一条 OS 线程卡住时，把 Go 世界继续往前推。&lt;/p&gt;
&lt;p&gt;第一篇我们讲过：G 是任务，M 是 OS 线程，P 是执行 Go 代码所必须持有的 runtime 资源。你可以把 P 类比成执行许可证。&lt;/p&gt;</description></item></channel></rss>