山高月小,水落石出。

Template text/template 完全学习

Go 语言 text/template 完全指南 text/template 是 Go 标准库自带的模板引擎,专门用于生成任意文本输出(HTML、配置文件、代码、Prompt 等)。下面从零讲到进阶,每个知识点都配可运行代码。 一、为什么需要模板引擎? 假设你要生成一段 Prompt: 1 2 // 字符串拼接 prompt := "你是一个" + role + "助手。请用" + style + "风格回答。" 一旦需求变复杂——要根据条件加 few-shot 示例、要循环生成示例、要做 A/B 实验——字符串拼接就变得难以维护。 模板引擎把"文案"和"逻辑"分离: 你是一个{{ .Role }}助手。请用{{ .Style }}风格回答。 代码只负责提供数据,模板负责渲染。 二、最小可运行示例 main.go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package main import ( "os" "text/template" ) func main() { tmpl := template.Must(template.New("hello").Parse("你好,{{ .Name }}!你今年 {{ .Age }} 岁。\n")) data := map[string]any{ "Name": "小明", "Age": 18, } if err := tmpl.Execute(os.Stdout, data); err != nil { panic(err) } } 运行: ...

October 13, 2026 · 8 min · zwdart
山高月小,水落石出。

Template 系统性学习

Go Template 系统性学习指南 Go 标准库的模板引擎是服务端渲染和数据驱动的文本生成工具,核心设计理念是将表现层与数据模型分离。Go 用同一套语法提供了两个包:text/template 用于通用文本生成,html/template 用于 HTML 输出并自带上下文感知的自动转义安全机制。网络服务中生成 HTML 必须使用 html/template。 一、基础用法:三步流程 模板使用的核心流程固定为三步:定义/解析模板 → 准备数据 → 执行渲染。 1.1 从字符串解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package main import ( "os" "text/template" ) type Inventory struct { Material string Count uint } func main() { // 步骤1:解析模板 tmpl := template.Must(template.New("test"). Parse("{{.Count}} items are made of {{.Material}}")) // 步骤2:准备数据 sweaters := Inventory{"wool", 17} // 步骤3:执行渲染 tmpl.Execute(os.Stdout, sweaters) // 输出:17 items are made of wool } template.Must 是对 Parse 错误的 panic 包装,适合在包初始化阶段使用。模板中只能访问结构体的导出字段(首字母大写),小写字段在模板侧不可达,会导致执行期报错或呈现零值。 ...

October 13, 2026 · 6 min · zwdart
山高月小,水落石出。

Goroutine sync.Mutex 互斥锁详解

Go 语言 sync.Mutex 互斥锁详解 sync.Mutex 是 Go 标准库 sync 包提供的互斥锁,用来保护共享资源,防止多个 goroutine 同时读写导致数据竞争。 一、为什么需要互斥锁? 先看一个没有锁的经典问题: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package main import ( "fmt" "sync" ) func main() { counter := 0 var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() counter++ // ⚠️ 数据竞争 }() } wg.Wait() fmt.Println("counter =", counter) } 多次运行,输出经常小于 1000,比如 987、993、1000 不固定。原因: ...

October 13, 2026 · 5 min · zwdart
山高月小,水落石出。

Goroutine Context 详细讲解

Go Context 详细讲解 context 是 Go 官方用来在 goroutine 之间传递取消信号、超时、截止时间和请求范围数据的标准库。它解决了并发编程中的核心问题:如何优雅地通知一组 goroutine 停止工作。 一、为什么需要 Context 1.1 没有 Context 的痛点 1 2 3 4 5 6 // 假设一个 HTTP 请求启动了 3 个 goroutine 处理子任务 func handler(w http.ResponseWriter, r *http.Request) { go fetchFromDB() // 用户断开连接,它还在跑 go callExternalAPI() // 超时了,它还在跑 go writeToCache() // 没人需要结果了,它还在跑 } 问题: 用户取消请求,goroutine 收不到通知,继续浪费资源 无法设置统一超时 无法在调用链中传递请求 ID、用户身份等数据 goroutine 泄漏 1.2 Context 的设计哲学 “一个请求,一棵 goroutine 树,一个 Context。” Context 是树形结构,根节点是 context.Background() 或 context.TODO() 父 Context 取消 → 所有子 Context 全部取消(级联) Context 是并发安全的,可被多个 goroutine 同时使用 Context 应该作为函数的第一个参数,命名为 ctx 二、Context 接口 1 2 3 4 5 6 7 8 9 10 11 12 13 type Context interface { // 返回 deadline 和是否设置了 deadline Deadline() (deadline time.Time, ok bool) // 返回一个 channel,在 Context 被取消时关闭 Done() <-chan struct{} // 返回取消原因(在 Done 关闭后才有意义) Err() error // 获取 key 对应的值 Value(key any) any } 关键点: ...

October 12, 2026 · 10 min · zwdart
山高月小,水落石出。

Goroutine Select

Go select 详解与可运行示例 一、本质:select 是什么 select 是 Go 里唯一的多路 channel 操作原语。它让一个 goroutine 同时等待多个 channel 操作,哪个先就绪就走哪个。没有它,你要么串行阻塞在单个 channel 上,要么自己开 goroutine 轮询。 一句话定位:switch 是对值的分支,select 是对通道就绪状态的分支。 1 2 3 4 5 6 7 8 9 10 select { case v := <-ch1: // ch1 有数据可读 handle(v) case ch2 <- x: // ch2 有接收者就绪 // 发送成功 case <-time.After(time.Second): // 超时 // ... default: // 所有 case 都没就绪 // 不阻塞,立即走这里 } 二、语法与语义规则 规则 1:case 必须是 channel 操作 合法:<-ch、ch <- v、v, ok := <-ch。 非法:普通表达式、函数调用(case f(): 不行)、常量比较。 ...

October 12, 2026 · 13 min · zwdart
山高月小,水落石出。

Goroutine Channel

Go Channel 详细讲解 Channel 是 Go 并发编程的核心,它是 goroutine 之间通信的管道,遵循 “不要通过共享内存来通信,而要通过通信来共享内存” 的设计哲学。 一、Channel 基础概念 1.1 什么是 Channel Channel 是一个类型化的管道,可以通过它发送和接收值: 1 2 3 ch := make(chan int) // 无缓冲 channel ch <- 42 // 发送 x := <-ch // 接收 1.2 创建方式 1 2 3 4 5 6 7 8 // 无缓冲 channel(同步) ch1 := make(chan int) // 有缓冲 channel(异步,容量为 3) ch2 := make(chan string, 3) // 声明(nil channel,无法使用,会永久阻塞) var ch3 chan int 1.3 三种操作 操作 语法 说明 发送 ch <- v 向 channel 写入 接收 v := <-ch 从 channel 读出 关闭 close(ch) 关闭 channel 二、无缓冲 vs 有缓冲 2.1 无缓冲 Channel(同步) 无缓冲 channel 的发送和接收必须同时准备好,否则阻塞。这叫"同步通信"。 ...

October 12, 2026 · 6 min · zwdart
这里有一直迷路的小兔子

Goroutine详细:sync.WaitGroup

sync.WaitGroup 是什么. sync.WaitGroup 是 Go 标准库中用于等待一组 goroutine 完成的同步原语。它解决的核心问题是:主 goroutine 需要知道其他多个 goroutine 何时全部执行完毕。 基本用法 1 2 3 4 5 6 7 8 9 10 11 var wg sync.WaitGroup for i := 0; i < 3; i++ { wg.Add(1) // 计数 +1 go func(id int) { defer wg.Done() // 计数 -1 // 做一些工作 }(i) } wg.Wait() // 阻塞,直到计数归零 为什么需要 Add 和 Done WaitGroup 内部维护一个计数器,它的语义是"还有多少个任务没完成": ...

October 11, 2026 · 1 min · zwdart
这里有一直迷路的小兔子

Goroutine详细:并发编程详细讲解

Go 并发编程详细讲解 Go 的并发模型基于 CSP(Communicating Sequential Processes),核心思想是:不要通过共享内存来通信,而要通过通信来共享内存。 下面从基础到进阶,系统讲解 Go 并发的所有核心概念,每个概念都配上可运行的示例。 一、Goroutine:轻量级线程 1.1 基础用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 3; i++ { fmt.Println(s, i) time.Sleep(100 * time.Millisecond) } } func main() { go say("world") // 启动一个 goroutine say("hello") // 主 goroutine 继续执行 time.Sleep(500 * time.Millisecond) // 等 goroutine 跑完 } 关键点: ...

October 10, 2026 · 12 min · zwdart