当前位置:首页 > 文章列表 > Golang > Go问答 > Go sync.Mutex 为什么不能复制

Go sync.Mutex 为什么不能复制

来源:17golang原创 2026-11-14 16:27:02 0浏览 收藏

Go 官方要求 sync.Mutex 在首次使用后不能复制。互斥关系依赖同一把锁的身份,复制结构体会得到另一份锁状态,无法继续代表原来那把锁。实际写法上,含 Mutex 的共享对象通常通过指针传递,读写方法使用指针接收者,并让同一份数据始终受同一个锁对象保护。

官方地址:https://pkg.go.dev/sync

先用指针方法绑定同一把锁

最小可用的设计是把锁和受保护字段放进一个对象,用指针方法封装访问。下面的 Counter 包含 mu 与 n;Add 和 Value 都使用同一个对象中的 mu,不把整个 Counter 返回给外部。

package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    mu sync.Mutex
    n  int
}

func (c *Counter) Add(delta int) {
    // 指针接收者让每次调用使用同一个对象中的锁。
    c.mu.Lock()
    defer c.mu.Unlock()
    c.n += delta
}

func (c *Counter) Value() int {
    // 读取也遵守相同的锁协议,只返回不含锁的数据。
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.n
}

func main() {
    c := &Counter{}
    var wg sync.WaitGroup
    for i := 0; i 

该示例的逻辑结果应为 400:四个协程各累加一百次,等待全部结束再读取。这里给出的是代码推导,不是本地执行记录。示例中 Counter 的零值可用,不需要通过复制另一把 Mutex 来初始化锁。

解释首次使用后禁止复制

Mutex 是有状态的同步对象。不要把它理解成一份“调用 Lock 的配置”,以为复制之后两边仍能共同排队。两个不同 Mutex 对象之间没有因为字段值相似就建立互斥关系。

复制已使用的锁可能把内部状态带进新对象,造成等待无法按原有关系结束。另一方面,即使复制时锁处于未锁定状态,两份锁也不是同一把锁,不能用来共同保护同一份共享数据。问题既有锁状态生命周期,也有数据保护关系,不能仅凭“现在没有上锁”判断可复制。

同一个Counter对象中锁与数据的绑定关系
静态结构图:两个指针方法关联同一对象内的 mu 和 n,表达共享锁身份,不表示执行顺序。

官方约束精确到“首次使用后”。独立创建多个全新的零值对象当然可以,每个对象拥有自己的锁与自己的数据。但如果字段里还有 map、slice 或指针,按值复制未必意味着数据独立。工程上让这类对象从构造到使用都通过指针流转,往往比要求所有调用方判断锁是否使用过更清楚。

识别值接收者和按值传递

方法写成值接收者时,调用会复制接收者。下面是需要避免的写法,仅用于说明复制位置:

// 错误示意:值接收者复制 Counter,锁住的是副本。
func (c Counter) AddWrong(delta int) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.n += delta
}

对于这个只含整数的例子,修改的是副本中的 n,原对象不会按预期更新。如果受保护字段换成 map,复制结构体又会保留对同一底层 map 的引用,而锁却变成不同副本;多个调用可能分别拿着自己的锁访问同一份数据。加锁语句存在,并不等于共享数据被同一把锁保护。

隐式复制也出现在按值参数、结构体赋值、把对象值装入接口或容器等位置。把方法改成指针接收者只是其中一部分,还需要保证调用链不再把整个已使用对象按值搬走。

位置风险常见处理
值接收者方法得到结构体副本使用指针接收者
按值函数参数参数复制含锁对象传入对象指针
对象赋值复制锁与字段共享指针或构造独立对象
业务快照整对象返回会携带锁只复制锁内读取的数据

安排容器与快照的结构

如果一组含锁对象需要放在可增长切片中,可考虑保存对象指针。对存放结构体值的切片进行扩容,会复制元素;如果元素中的锁已使用过,就不应再依赖这种搬移。遍历时把元素复制到循环变量,也会带来相同类型的问题。

使用对象指针切片时,切片复制的是指针,所指对象本身的身份保持不变。不过容器本身的并发修改仍需要单独管理,不能因为元素有锁就认为 append 或替换元素也自动安全。

快照也应只输出业务数据。上面的 Value 就返回 int。如果未来需要返回多个字段,可定义一个不含同步字段的快照结构体,在锁内读取字段,再返回快照。对于 map、slice 这样的引用型内容,是否深拷贝取决于快照的隔离要求。

容器指针与业务快照的职责结构
静态说明图:容器保存对象指针,快照只包含业务字段;共享对象与可复制的数据各有职责。

把 Mutex 字段改成 Mutex 指针也不是通用修复:它可能让不同数据副本共享一把锁,还引入 nil 和初始化约束。应先明确哪些数据属于同一个共享对象,再选择对象引用方式。

用工具提示辅助定位复制

Go 工具链的 copylocks 分析关注锁被错误按值传递的情况。项目开发时可用 go vet 的相关提示定位值接收者、参数和赋值等复制位置;动态竞争检测则关注被实际执行路径中的竞争。工具没有报告不能替代对象身份与锁协议的设计。

采用这种设计时,维护者最直接的收益是能回答两个具体问题:每份共享数据归哪个对象,所有并发访问是否经过同一把锁。性能与布局优化应在这个关系明确之后再做,不需要因为一条复制问题就替换全部同步机制。

复制 Mutex 指针会复制锁吗?

复制指针仍指向同一个 Mutex,不会复制所指锁对象。但对象是否存活、数据是否仍由它保护,需要由接口约定保证。

Unlock 后就能复制整个结构体吗?

不能。官方规则是首次使用后不能复制,并不以当前是否锁定为判断条件。

每个方法都有 Lock 为什么还有问题?

先确认锁是否来自同一个对象,以及共享字段是否通过副本、map 或指针继续指向同一份数据。不同锁不会相互排斥。

参考:Go Mutex 使用约定、copylock 分析器说明。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go net.IP.IsUnspecified 怎样区分 0.0.0.0、:: 和普通地址Go net.IP.IsUnspecified 怎样区分 0.0.0.0、:: 和普通地址
上一篇
Go net.IP.IsUnspecified 怎样区分 0.0.0.0、:: 和普通地址
Go bytes.Cut 怎样同时取分隔符两侧的字符串
下一篇
Go bytes.Cut 怎样同时取分隔符两侧的字符串
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1022次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1025次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1163次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1131次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    968次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码