<?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>Scheduler on 我的技术博客</title><link>https://buvidk1234.github.io/tags/scheduler/</link><description>Recent content in Scheduler on 我的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 18 Aug 2026 00:19:00 +0800</lastBuildDate><atom:link href="https://buvidk1234.github.io/tags/scheduler/index.xml" rel="self" type="application/rss+xml"/><item><title>Go Runtime（十）：Timer实现</title><link>https://buvidk1234.github.io/posts/go-runtime-10-timer/</link><pubDate>Tue, 18 Aug 2026 00:19:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-10-timer/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。Go 1.23 重写了 Timer Channel 的可回收性与 Stop/Reset 保证；阅读旧版本分析时必须先确认版本边界。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Timer 不是一个独立后台 goroutine 加一张全局表。Go 1.26 把真实时间 Timer 组织在每个 P 的四叉最小堆中，由调度器和 netpoll 共同决定睡到何时、何时执行到期回调。&lt;/p&gt;
&lt;p&gt;它同时服务 &lt;code&gt;time.Sleep&lt;/code&gt;、&lt;code&gt;time.Timer&lt;/code&gt;、&lt;code&gt;time.Ticker&lt;/code&gt;、&lt;code&gt;AfterFunc&lt;/code&gt; 和网络 Deadline。难点不只是“按时间排序”，而是并发 Stop/Reset、跨 P 修改、周期跳期、Channel 旧值以及 GC 可达性。&lt;/p&gt;
&lt;h2 id="time包如何进入runtime"&gt;time包如何进入runtime&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;time&lt;/code&gt; 包通过 runtime 提供的私有入口创建和修改 Timer：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&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;time.Sleep -&amp;gt; runtime.timeSleep
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;time.NewTimer -&amp;gt; runtime.newTimer(sendTime, channel)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Timer.Stop -&amp;gt; runtime.stopTimer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Timer.Reset -&amp;gt; runtime.resetTimer
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;time.AfterFunc -&amp;gt; runtime.newTimer(goFunc, nil)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;net deadline -&amp;gt; runtime timer callback&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;&lt;a href="https://github.com/golang/go/blob/go1.26.2/src/time/sleep.go#L143"&gt;&lt;code&gt;time.NewTimer&lt;/code&gt;&lt;/a&gt; 的源码仍写 &lt;code&gt;make(chan Time, 1)&lt;/code&gt;。默认模式下 runtime 借助 &lt;code&gt;hchan.timer&lt;/code&gt; 对外呈现同步、容量为零的语义；设置 &lt;code&gt;GODEBUG=asynctimerchan=1&lt;/code&gt; 才恢复 Go 1.23 以前的异步 Timer Channel 行为。因此不能只看 &lt;code&gt;make&lt;/code&gt; 参数推断 &lt;code&gt;cap(t.C)&lt;/code&gt; 和 Stop/Reset 语义。&lt;/p&gt;</description></item><item><title>Go Runtime（二）：GMP与Goroutine调度</title><link>https://buvidk1234.github.io/posts/go-runtime-02-scheduler/</link><pubDate>Tue, 18 Aug 2026 00:11:00 +0800</pubDate><guid>https://buvidk1234.github.io/posts/go-runtime-02-scheduler/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文源码基线为 &lt;strong&gt;Go 1.26.2、Linux/amd64&lt;/strong&gt;。调度器变化频繁；函数名、队列容量和分支顺序均以固定版本源码为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go 调度器解决的核心问题是：怎样把大量 goroutine 映射到较少的 OS 线程，同时控制竞争、阻塞和调度延迟。&lt;/p&gt;
&lt;p&gt;理解调度器不需要记住所有函数。先抓住三件事：执行资格属于 P，等待发生在 G 上，真正运行机器指令的是 M。&lt;/p&gt;
&lt;h2 id="gmp模型"&gt;GMP模型&lt;/h2&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&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;G（goroutine）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 栈、入口函数、调度现场、状态、等待原因
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;M（machine）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; OS线程、当前G、g0、信号处理上下文
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P（processor）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 本地运行队列、runnext、mcache、timer、执行资格&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;抽象关系如下：&lt;/p&gt;
&lt;details class="code-fold" open&gt;
&lt;summary&gt;TEXT&lt;/summary&gt;&lt;div class="highlight"&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; P.local runq
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [G][G][G][G]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;M(OS thread) + P ----&amp;gt; 执行某个 G
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; P.runnext&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;
&lt;p&gt;M 必须绑定 P 才能执行普通 Go 代码。没有 P 的 M 可以停在线程阻塞、syscall、cgo 或 runtime 的特定路径中，但不能继续执行任意用户 Go 代码。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GOMAXPROCS&lt;/code&gt; 控制 P 的数量。它限制的是并行执行 Go 代码的能力，不限制 goroutine 或 OS 线程的总数。&lt;/p&gt;</description></item></channel></rss>