当前位置:首页 > 文章列表 > Golang > Go教程 > Go sync.Map.CompareAndDelete 怎样按旧值安全删除

Go sync.Map.CompareAndDelete 怎样按旧值安全删除

来源:17golang原创 2026-10-26 16:38:19 0浏览 收藏

sync.Map.CompareAndDelete 是 Go 标准库 sync 包提供的原子操作接口,要实现按指定旧值安全删除元素,直接传入待操作的键、以及你预期匹配的旧值作为两个入参即可。方法内部会全程以原子态完成校验,只有当当前 sync.Map 里对应键存储的实际值和你传入的旧值完全相等时,才会真正执行删除动作,整个操作过程不会被其他 Goroutine 的并发读写打断,天然解决了普通场景下先取值、再判等、再删除的多步操作竞态问题。

CompareAndDelete 不需要你额外加互斥锁,就能在并发场景下,安全完成「旧值匹配校验+条件删除」的组合操作,确保只有符合预期旧值的元素才会被移除。

我在清理并发缓存时遇到过一个很隐蔽的问题:先 Load 看到旧值,再调用 Delete,中间如果另一个 goroutine 已经写入新值,清理逻辑就可能把新值一起删掉。这个场景适合用 sync.Map.CompareAndDelete,让“当前值仍等于我看到的旧值”成为删除条件。

它的判断结果很直接:返回 true 表示按 key 找到相等的旧值并完成删除,返回 false 表示没有发生删除。本文只讨论这个条件删除方法,不把它扩展成 sync.Map 的全面选型。

先看普通 Delete 为什么有并发窗口

下面的写法把读取和删除拆成了两个独立动作。它能工作,但不能保证删除的仍是刚才读到的那一份值。

package main

import "sync"

func removeIfStale(m *sync.Map, key string) {
	// 先读到旧值;这次读取和后面的删除之间存在并发窗口。
	old, ok := m.Load(key)
	if !ok {
		return
	}
	_ = old

	// 另一个 goroutine 可能已经 Store 了新值,此时 Delete 会无条件删除当前值。
	m.Delete(key)
}

例如缓存清理者读到 token-v1,更新者随后写入 token-v2,清理者再执行 Delete,最终连新值也会消失。问题不是 Delete 本身错误,而是它没有携带“我删除的是哪一个旧值”的条件。

sync.Map 的 key、当前值、old、CompareAndDelete 和 deleted 返回值静态关系图
图1:CompareAndDelete 的键值条件、比较接口和删除结果说明图,不是运行截图或运行证据。

用 CompareAndDelete 绑定 key 和 old

CompareAndDelete(key, old) 会把 key 和 old 一起交给 sync.Map。只有当前映射值与 old 相等时,删除才会发生;因此它把原本的“读后无条件删”变成了“确认还是旧值才删”。

package main

import "sync"

func removeVersion(m *sync.Map, key, version string) bool {
	// old 是本次清理认可的版本,只有当前值仍等于它才允许删除。
	return m.CompareAndDelete(key, version)
}

func main() {
	var cache sync.Map
	cache.Store("profile:42", "v1")

	// 返回 true 表示 v1 仍是当前值,并且已经被条件删除。
	_ = removeVersion(&cache, "profile:42", "v1")

	// 若期间换成 v2,这里返回 false,v2 会被保留下来。
	cache.Store("profile:42", "v2")
	_ = removeVersion(&cache, "profile:42", "v1")
}

这里最值得记住的是返回值,而不是把它当成普通的“删除命令”。true 可以作为清理成功、缓存失效或后续通知的依据;false 则说明当前值已经变化、key 不存在,或条件没有命中,调用方应避免把它误报成删除成功。

处理不可比较旧值和不存在键

CompareAndDelete 需要比较 old,因此 old 必须是可比较类型。字符串、整数、指针和只含可比较字段的结构体可以直接使用;切片、map、函数等不可比较值不应作为 old。把它们装进 any 并不会改变这个限制,实际比较时仍可能触发 panic。

另一个容易忽略的边界是 key 不存在。即使 old 是 nil interface,当前没有这个 key 时仍返回 false,不会凭空产生一次删除。可以把四种结果按下面的方式理解:

当前状态old 条件结果
key 存在当前值等于 old删除并返回 true
key 存在当前值不等于 old保留当前值,返回 false
key 不存在old 为任意可比较值不删除,返回 false
old 不可比较无法建立合法比较调用前应改用可比较快照,避免 panic
CompareAndDelete 的匹配删除、旧值不匹配、键不存在和不可比较旧值边界图
图2:CompareAndDelete 四类结果边界静态说明图,不是运行截图或运行证据。

把条件删除放进缓存清理路径

在工程代码里,建议把“旧值快照”设计成可比较的版本号、状态字符串或指针,而不是直接把一整份可变对象塞进 old。这样清理动作只关心版本是否仍然匹配,更新者写入新版本后,旧清理请求自然会返回 false。

package main

import "sync"

type Cache struct {
	items sync.Map // key -> 可比较的版本标识
}

func (c *Cache) RemoveVersion(key, expected string) bool {
	// 条件不满足时保留新版本,调用方可记录一次“已被更新”的结果。
	return c.items.CompareAndDelete(key, expected)
}

如果清理失败,通常不应立刻重试同一个 old。先重新读取当前版本,再决定是放弃、重算清理条件,还是让新的失效事件处理。若业务需要严格的多字段原子更新,也要重新评估数据结构;sync.Map 只适合它擅长的并发读写场景,不能替代所有带业务不变量的普通 map 加锁方案。

相关问题

CompareAndDelete 和 Delete 的核心区别是什么?

Delete 只按 key 无条件删除当前值;CompareAndDelete 还要求当前值等于 old,适合防止旧清理请求误删新写入。

返回 false 是不是说明发生了错误?

不一定。它可能表示 key 已不存在,也可能表示值已经变化。是否记录为错误,要看业务把“未命中清理条件”视为正常竞争还是异常状态。

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