“不要传输数据,而是转移数据所有权”
哈喽!今天心血来潮给大家带来了《“不要传输数据,而是转移数据所有权”》,想必大家应该对Golang都不陌生吧,那么阅读本文就都不会很困难,以下内容主要涉及到,若是你正在学习Golang,千万别错过这篇文章~希望能帮助到你!
我了解到 Golang 通道实际上比该语言提供的许多替代方案慢。当然,它们确实很容易掌握,但由于它们是高级结构,因此会带来一些开销。
阅读了一些相关文章,我发现有人对这里的频道进行了基准测试。他基本上说通道可以传输 10 MB/s,这当然必须取决于他的硬件。然后他说了一些我没有完全理解的话:
如果您只想使用通道快速移动数据,则移动它 1 一次一个字节是不明智的。您对频道的真正用途是 移动数据的所有权,在这种情况下,数据速率可以是 实际上是无限的,具体取决于您的数据块的大小 转移。
我在几个地方看到过这种“移动数据所有权”,但我还没有看到一个可靠的例子来说明如何做到这一点,而不是移动数据本身。
我想看一个例子来理解这个最佳实践。
正确答案
通过通道移动数据:
c := make(chan [1000]int) // spawn some goroutines that read from this channel var data [1000]int // populate the data // write data to the channel c <- data
正如您所提到的,这里的潜在问题是您正在移动大量数据,因此您可能会进行过多的内存复制。
您可以通过发送引用类型来防止这种情况,例如通道上的指针或切片:
c := make(chan []int) // spawn some goroutines that read from this channel var data [1000]int // populate the data // write a reference to data to the channel c <- data[:]
所以我们只是进行了完全相同的数据传输,但减少了内存复制,对吧?好吧,这里有一个潜在的问题:您通过通道发送了对 data
的引用,但是即使在发送之后,data
值仍然可以在当前范围内访问:
// write a reference to data to the channel c <- data[:] // start messing with data data[0] = 999 data[1] = 1234 ...
这段代码可能刚刚引入了潜在的数据竞争,因为无论谁从通道读取该切片,都可能在您开始修改它的同时对其进行处理。
传递所有权的想法是,在您给出对某物的引用后,您也承认了对该事物的所有权,并且不会使用它。只要我们在给出引用(在通道上发送切片)之后不使用 data
,那么我们就已经正确地传递了所有权。
这个问题是共享状态一般问题的延伸。例如,与 rust 不同的是,go 没有语言结构来正确控制共享状态。为了减少出现这些错误的机会,您可以应用一些策略:
- 避免在通道上传递引用:在上面的示例中,一旦我们开始使用切片通过引用传递数据,就会出现问题。这样做只是为了减少内存应对量。除非有实际原因进行此优化(测量了有价值的性能差异),否则可以完全避免。尽管如此,go 中仍有一些数据类型本质上是引用(例如映射和切片)。如果这些类型必须在通道上传递,则可以使用其他策略。
- 将数据创建逻辑分离为函数:在上面的示例中,我们可以重构代码:
func senddata(c chan []int) { var data [1000]int // populate the data // write a reference to data to the channel c <- data[:] }
c := make(chan []int) // spawn some goroutines that read from this channel // send some data sendData(c)
错误使用 data
的可能性仍然存在,但现在它被隔离为一个具有明确意图的小函数。理论上,隔离应该使代码更容易理解,更明显 data
的正确用法是什么,并且更少的更改会与其产生潜在的交互。
- 不要将数据管道与持久状态混合:数据管道是指两个或多个并发例程,数据通过通道在这些例程之间流动。扩展上一点,使自有引用的创建尽可能靠近它们进入数据管道的位置。在 goroutine 接收数据的位置和再次发送数据或使用数据的位置之间留出尽可能紧密的空间。在所有权的一般规则中,只有当您目前拥有某物的完全所有权时,您才能转让该物的所有权。根据此规则,您应尽可能避免在并非发送前刚刚创建引用数据的频道上发送任何引用。如果您引用任何持久或全局状态,那么确保尊重所有权就会变得更加困难。
通过将引用的创建和所有权的转移保持在一个隔离的全局函数中,应该更难犯错误。那么违反所有权规则的唯一方法是:
- 泄露对全局状态的引用
- 尝试消除全局变量和全局状态
- 泄漏对引用类型参数状态的引用
- 不要在数据发送函数中使用任何引用类型参数
- 发送参考后修改参考数据
- 将发送操作放在函数的最后。如有必要,您可以将发送放入延迟调用中。
没有完美的解决方案来消除所有共享状态问题(即使在 rust 中,它们在实践中有时也存在),但我希望这些策略能够帮助您思考如何解决这个问题。
Hymns For Disco's answer 很好,但我发现写出好的简短有时会回答一个有趣的挑战,我想我有一个类比在这里会有所帮助。
将您的数据想象成占据仓库,每个仓库的大小都相当于一个大型城市街区。您有一千个仓库,分布在许多国家和城市。
你们有五名技术精湛的技术人员,每个人都能把一件事做得非常好。您需要所有技术人员来操作所有数据。不幸的是,每个技术人员都讨厌其他四个人,如果他们中的任何一个出现在仓库中,他们就不会做任何工作(甚至可能会试图杀死其他人)。
解决这个问题的一种方法是建造五个额外的仓库,并将五名技术人员分别安置在新的五个仓库中。然后,您可以将 1000 个仓库中每个仓库的全部内容运送到各个备件,一次一个,然后在每位技术人员完成处理后将内容移回;您可以通过将内容移动到工作仓库 #1、然后到 #2、然后到 #3 等来优化仓库内容移动,并且仅在准备离开后才将其移回原始仓库#5。但显然,这需要大量的运输和物流,并且需要大量的时间和金钱来完成所有这些批量运输。即使每个技术人员可以在一天之内完全处理整个仓库,也需要数年时间和大量资金才能完成所有工作。
或者,您可以派遣五名技术人员。将它们发送到仓库 (wh) 1-5。当 tech#1 处理完 wh#1 后,将他移动到 wh#2,除非 tech#2 仍然在那里;如果这是下一个免费的,请将他移动到 wh#6。
我们正在移动小而轻的“做工作的人”,而不是大而重的“占据该人工作空间的东西”。总体成本要低得多。不过,我们必须小心,不要意外地让技术人员相遇。
还要注意,如果数据本身又小又轻且易于移动,那么这种花哨的解决方案(即仔细考虑谁在何时可以访问哪些数据)就无济于事。在小数据情况下,我们不妨移动数据,而不是工人。
今天关于《“不要传输数据,而是转移数据所有权”》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

- 上一篇
- 由后端PHP到前端开发的转换之程

- 下一篇
- Sync WaitGroup 不等待 goroutine 分配指针
-
- Golang · Go问答 | 1年前 |
- 在读取缓冲通道中的内容之前退出
- 139浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 戈兰岛的全球 GOPRIVATE 设置
- 204浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何将结构作为参数传递给 xml-rpc
- 325浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何用golang获得小数点以下两位长度?
- 477浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何通过 client-go 和 golang 检索 Kubernetes 指标
- 486浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 将多个“参数”映射到单个可变参数的习惯用法
- 439浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 将 HTTP 响应正文写入文件后出现 EOF 错误
- 357浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 结构中映射的匿名列表的“复合文字中缺少类型”
- 352浏览 收藏
-
- Golang · Go问答 | 1年前 |
- NATS Jetstream 的性能
- 101浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何将复杂的字符串输入转换为mapstring?
- 440浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 相当于GoLang中Java将Object作为方法参数传递
- 212浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何确保所有 goroutine 在没有 time.Sleep 的情况下终止?
- 143浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 可图AI图片生成
- 探索快手旗下可灵AI2.0发布的可图AI2.0图像生成大模型,体验从文本生成图像、图像编辑到风格转绘的全链路创作。了解其技术突破、功能创新及在广告、影视、非遗等领域的应用,领先于Midjourney、DALL-E等竞品。
- 14次使用
-
- MeowTalk喵说
- MeowTalk喵说是一款由Akvelon公司开发的AI应用,通过分析猫咪的叫声,帮助主人理解猫咪的需求和情感。支持iOS和Android平台,提供个性化翻译、情感互动、趣味对话等功能,增进人猫之间的情感联系。
- 14次使用
-
- Traini
- SEO摘要Traini是一家专注于宠物健康教育的创新科技公司,利用先进的人工智能技术,提供宠物行为解读、个性化训练计划、在线课程、医疗辅助和个性化服务推荐等多功能服务。通过PEBI系统,Traini能够精准识别宠物狗的12种情绪状态,推动宠物与人类的智能互动,提升宠物生活质量。
- 17次使用
-
- 可图AI 2.0图片生成
- 可图AI 2.0 是快手旗下的新一代图像生成大模型,支持文本生成图像、图像编辑、风格转绘等全链路创作需求。凭借DiT架构和MVL交互体系,提升了复杂语义理解和多模态交互能力,适用于广告、影视、非遗等领域,助力创作者高效创作。
- 19次使用
-
- 毕业宝AIGC检测
- 毕业宝AIGC检测是“毕业宝”平台的AI生成内容检测工具,专为学术场景设计,帮助用户初步判断文本的原创性和AI参与度。通过与知网、维普数据库联动,提供全面检测结果,适用于学生、研究者、教育工作者及内容创作者。
- 32次使用
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- Golang取得代码运行时间的问题
- 2023-02-24 501浏览
-
- 请问 go 代码如何实现在代码改动后不需要Ctrl+c,然后重新 go run *.go 文件?
- 2023-01-08 501浏览
-
- 如何从同一个 io.Reader 读取多次
- 2023-04-11 501浏览