Go 的 build tag 与条件编译:跨平台代码隔离的最佳实践

在 Go 里做跨平台系统编程,靠 `runtime.GOOS` 写运行期分支并不能解决编译期符号冲突。本文以文件锁封装为例,梳理 build tag、文件命名规则、Unix 与 Windows 差异,以及实现时最容易踩到的类型和语义问题。 跨平台 Go 代码一旦碰到系统调用,真正难点往往不是运行时分支,而是怎样避免编译期就被平台符号拖垮。

Go 的 build tag 与条件编译:跨平台代码隔离的最佳实践

别让路径穿越毁了服务:Go 中的 os.Root 实践

路径穿越一直是文件上传、下载和静态资源处理里的高风险问题。Go 1.24 新增的 os.Root 把目录边界控制下沉到系统调用层,能同时应对 ../ 逃逸、符号链接绕过和 TOCTOU 风险。本文结合原理与示例,梳理它在实际服务中的用法和适用边界。

别让路径穿越毁了服务:Go 中的 os.Root 实践

Go 语言中 time.After、Timer 和 Ticker 怎么选?

Go 里的超时与定时逻辑看似简单,真正容易出问题的往往是循环分配、Timer 重置语义和 Ticker 退出方式。本文按 Go 1.23 前后差异拆解 time.After、Timer、Ticker 与 AfterFunc 的适用边界,帮助你按场景选型并避开常见误用。 超时控制、延迟执行、定时轮询在 Go 服务中几乎无处不在,但真正容易出问题的,往往不是 API 会不会用,而是生命周期、版本语义和并发边。

Go 语言中 time.After、Timer 和 Ticker 怎么选?

Go 切片截取后,为什么大数组迟迟无法释放?

Go 中把大切片截成小切片后,内存迟迟不降,通常不是 GC 不工作,而是小切片还在引用原来的大数组。本文从切片结构、GC 可达性、复制方案、三索引切片的边界和排查思路几方面说明,帮助你判断什么时候该直接截取,什么时候必须主动复制。

Go 切片截取后,为什么大数组迟迟无法释放?

Go runtime.AddCleanup:更稳妥的兜底清理

Go 1.24 新增的 `runtime.AddCleanup`,把“观察对象生命周期”和“执行资源释放”拆开,减少了 `SetFinalizer` 常见的对象复活、引用环和依赖顺序问题。但它依旧不是确定性析构,真正可靠的释放路径仍然是 `Close` 加 `defer`。 文件句柄、Socket 和 C 内存这类资源,一旦调用方漏掉 `Close`,就可能把问题拖成真正的泄漏。

Go runtime.AddCleanup:更稳妥的兜底清理

Python 实现 Excel 页面布局与打印设置自动化

用 Python 生成 Excel 时,只把数据写进去往往不够,真正到打印预览阶段,边距、方向、表头重复和缩放才决定成品是否可用。本文基于 Spire.XLS for Python,梳理几项最常用的页面设置,并给出可直接套用的综合示例。

Python 实现 Excel 页面布局与打印设置自动化

Go 语言中 nil channel:用一个变量动态控制 select 分支

在 Go 并发里,把 channel 变量设为 nil 是一种很实用的 select 控制手段。它不需要额外语法,却能在同一套循环中动态启停接收、发送和多路合并分支。关键在于分清 nil、关闭 channel 与 default 忙循环的边界,才能既简化状态机,又避免死锁和空转。

Go 语言中 nil channel:用一个变量动态控制 select 分支

Go 项目中 GOMAXPROCS 还需要手动设置吗?

GOMAXPROCS 常被误解为“限制 goroutine 数量”或“固定 CPU 核数”,在容器里更容易因为宿主机与配额不一致而配错。结合 Go 1.25 之后的新默认行为,本文梳理它到底控制什么、何时不用再手动设,以及哪些场景仍应基于压测与指标做显式调整。

Go 项目中 GOMAXPROCS 还需要手动设置吗?

Go 语言中 HTTP 响应体为什么必须关闭?

在 Go 里,`resp.Body.Close()` 不是可有可无的习惯写法,而是 `net/http` 明确交给调用方完成的收尾责任。真正容易混淆的是:关闭响应体解决的是资源释放,想让 HTTP/1.x 连接更有机会复用,通常还需要把响应读到 EOF。

Go 语言中 HTTP 响应体为什么必须关闭?

Go 切片传参后 append,为什么有时会改到外层,有时又不会?

Go 切片传参后执行 append,结果之所以“有时影响外层、有时又不影响”,关键不在“引用类型”或“值传递”这类单句判断,而在切片头复制与底层数组复用。本文用几个短例子讲清 len、cap 和共享数组的关系,并给出业务代码里更稳妥的写法。

Go 切片传参后 append,为什么有时会改到外层,有时又不会?

面试题:Go 语言中读多写少,选 RWMutex 是万能的吗?

读多写少并不等于可以直接把互斥锁换成 `sync.RWMutex`。在高并发和多核环境下,读写锁的读路径同样要做原子写入,既可能引发缓存抖动,也会在写入到来时让新读请求排队。本文结合 Go 1.19+ 的实现细节,说明 `RWMutex` 何时有效、何时退化,以及 `atomic.Value` 在极端读多写少场景中的工程替代方案。

面试题:Go 语言中读多写少,选 RWMutex 是万能的吗?

从 bytes.Buffer 到 io.Pipe:实现零拷贝数据流转的实践

在 Go 中把一个 io.Writer 的输出接到 io.Reader,很多人会先想到 bytes.Buffer。但数据一旦上到 GB 级,缓存整段内容很容易把内存顶满。本文围绕 io.Pipe 的同步管道机制,梳理它的工作方式、常见死锁原因、错误传递方法,以及它和 bytes.Buffer 的适用边界。

从 bytes.Buffer 到 io.Pipe:实现零拷贝数据流转的实践