当前位置:首页 > 文章列表 > Golang > Go问答 > Go sync.Mutex 应该嵌入值还是通过指针接收者使用

Go sync.Mutex 应该嵌入值还是通过指针接收者使用

来源:17golang原创 2026-09-09 10:39:08 0浏览 收藏

如果一个 Go 结构体内部有 sync.Mutex,常见且稳妥的组合是:把锁作为值字段嵌入,把结构体的方法写成指针接收者。也就是 sync.Mutex 负责保存这一个对象的锁状态,*Counter 负责让方法始终修改同一个对象,而不是复制出一个新接收者。

结论很简单:优先使用 sync.Mutex 值字段 + 指针接收者;不要用值接收者操作包含锁的结构体,也不要为了“避免复制”盲目把锁改成 *sync.Mutex
要点速览
  • sync.Mutex 的零值可直接使用,通常不需要 new 或构造函数。
  • 包含 Mutex 的结构体方法使用指针接收者,避免调用时复制锁和受保护的数据。
  • 结构体一旦开始使用锁,就不要再按值传参、赋值、返回或放入会复制它的容器。

锁按值嵌入,方法按指针接收者

下面的写法把锁和计数值放在同一个对象里。匿名嵌入让方法可以写成 c.Lock(),但真正重要的是 AddValue 的接收者都是 *Counter

package main

import "sync"

// Counter 把计数值和保护它的锁绑定在同一个对象中。
type Counter struct {
    sync.Mutex
    value int
}

// Add 必须用指针接收者,避免复制 Mutex 和 value。
func (c *Counter) Add(delta int) {
    c.Lock()
    defer c.Unlock() // 无论函数如何返回,都释放临界区
    c.value += delta
}

// Value 也用指针接收者,读取和写入使用同一把锁。
func (c *Counter) Value() int {
    c.Lock()
    defer c.Unlock()
    return c.value
}

声明 var c Counter 后,锁的零值就是未加锁状态,可以直接调用方法。多个 goroutine 应共享同一个 *Counter,这样它们争用的是同一个锁,而不是各自副本中的锁。

值嵌入 Mutex 与指针接收者共同保护同一个 Counter 对象的结构关系
图1:值字段保存 Mutex,指针接收者让业务方法始终落在同一个 Counter 对象上。

为什么值接收者会破坏这个设计

值接收者的语法看起来更简短,但每次调用都会得到一份接收者副本。对于普通不可变小对象,这可能没有问题;对于包含锁的结构体,副本同时包含一份复制后的锁状态和一份复制后的数据。

// 错误示例:c 是 Counter 的副本,不是调用者手里的原对象。
func (c Counter) Add(delta int) {
    c.Lock()
    defer c.Unlock()
    c.value += delta // 改的是副本,调用者看不到这次修改
}

func use() {
    var c Counter
    c.Add(1) // 编译器可以取得 c 的地址,但方法声明仍会复制值
}

这里有两个问题:第一,value 的修改写入副本;第二,锁也被复制,调用者和方法内部可能并没有用同一把锁保护同一份数据。官方文档要求 Mutex 首次使用后不得复制,Go 的代码审查建议也明确指出,含有同步字段的结构体应使用指针接收者。

把锁写成 *sync.Mutex 就一定更好吗

不一定。指针字段能让多个结构体显式共享一把锁,但也引入了锁的初始化和生命周期问题:

写法默认状态主要风险适用判断
sync.Mutex零值可用对象使用后不能复制对象自己拥有并保护自己的状态,优先选择
*sync.Mutex默认是 nil忘记初始化会 panic;复制指针会共享锁确实需要外部注入或多个对象共享同一锁时再用

如果选择指针字段,初始化应当集中处理,不能让调用方猜测锁是否为空:

type SharedCounter struct {
    mu    *sync.Mutex
    value int
}

func NewSharedCounter() *SharedCounter {
    return &SharedCounter{
        mu: new(sync.Mutex), // 明确创建锁的所有权
    }
}

func (c *SharedCounter) Add(delta int) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value += delta
}

这个版本只有在“锁需要被注入或共享”时才有价值。若锁和数据天然属于同一个对象,值字段更容易保证零值可用,也不容易出现 nil 锁。

实际项目中重点检查哪些复制路径

除了值接收者,还要留意函数参数、返回值、结构体赋值和把对象放进接口时的复制。一个已经加过锁的 Counter 不应再写成 func f(c Counter),也不应通过 c2 := c1 制造第二份值。更安全的边界是传递 *Counter,并让对象的所有权和并发访问方式保持清晰。

func update(c *Counter) {
    c.Add(1)
}

func main() {
    c := &Counter{}
    update(c) // 共享同一对象和同一把锁
}

// go vet -copylocks ./...
// 用静态检查尽早发现可能复制 Mutex 的代码路径。
复制包含 Mutex 的结构体会形成副本锁,指针传递则继续使用同一对象
图2:比较按值复制与传递指针的两条路径,判断锁和被保护数据是否仍然属于同一对象。

最后可以用四个问题做快速判断:锁是否只服务一个对象?若是,使用值字段;方法是否会改变状态或包含锁?使用指针接收者;对象是否已使用过锁?禁止按值复制;是否真的需要共享锁?只有这时才考虑 *sync.Mutex,并集中初始化。

相关问题

Mutex 可以放在结构体的第一个字段吗?

可以。字段顺序通常不是并发正确性的关键,关键是锁和受保护的数据要由同一组方法一致地访问,并避免复制已经使用过的结构体。

读方法一定要用指针接收者吗?

只要结构体包含 Mutex 或其他不可复制同步状态,就应保持指针接收者,即使方法看起来只是读取。这样可以避免读方法本身复制接收者,也让整个类型的调用约定保持一致。

参考资料

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
VS Code tasks.json 怎么把输入参数传给任务VS Code tasks.json 怎么把输入参数传给任务
上一篇
VS Code tasks.json 怎么把输入参数传给任务
RAG 检索结果太多时怎么用元数据过滤缩小上下文
下一篇
RAG 检索结果太多时怎么用元数据过滤缩小上下文
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    41次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    193次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    129次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    60次使用
  • Generrated:DALL·E 2/3 AI绘画提示词灵感库与图像对比平台
    Generrated
    Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
    42次使用