背景介绍
环境
- 操作系统
- 本机:MacOS 15
- 远程: 阿里云封装的centos
- go版本
- 本机:go1.21.0
- 远程:go1.14.12
- IDE:GoLand
- 辅助:ChatGPT 4o
1 | $ cat /etc/os-release |
必做:封装一个数据结构 MyMap
作业内容
封装一个数据结构 MyMap,实现并发安全的 Load,Store,Delete,LoadAndDelete,LoadOrStore 几个 API(禁止使用 sync.Map),不用考虑性能
参考
1 | package main |
实现
1 | package main |
v1优化 by ChatGPT-4o
改进建议
读写锁:在 Load
操作中,使用读锁
RLock
而不是写锁
Lock
,这样在读取时不会阻塞其他读取操作。
具体实现
1 | package main |
v2优化 by ChatGPT-4o
改进建议
- 减少锁的粒度:我们可以尝试进一步优化锁的使用方式,以提高并发性能。例如,可以在
LoadOrStore
和LoadAndDelete
方法中先尝试使用读锁进行初步检查,再在需要时升级为写锁。 - 使用
sync.Map
中的一些优化思想:虽然不能使用sync.Map
,但我们可以参考一些它的优化思想,比如减少写锁的持有时间。
实现
1 | package main |
选做:比较MyMap与sync.Map性能差异
作业内容
写 benchmark,比较 MyMap 与 sync.Map 的同名函数性能差异(LoadAndDelete 可以不用比较,该函数在 Go 1.15 引入,我们的作业环境是 1.14.12),输出相应的性能报告(注意,你应该使用 RunParallel)
benchmark编写
两者写法类似
1 | package main |
benchmark环境
- 操作系统
- 本机:MacOS 15
- go版本
- 本机:go1.21.0
benchmark运行
1 | ❯ go test -bench . |
选做:使用 channel 实现一个 trylock
作业内容
模板 trylock.go
1 | package main |
实现
1 | package main |
选做:修复deadlock.go的死锁
作业内容
1 | package main |
运行结果
1 | fatal error: all goroutines are asleep - deadlock! |
deadlock分析
程序在DoMyJob中获取了读锁,而finishJob尝试去获取写锁,但写锁无法在持有读锁时获取
deadlock修复
提前释放读锁即可
1 | package main |
选做:实现MyContext
作业内容
在 context 学习过程中,我们知道,在子节点中 WithValue 的数据,父节点是查不到的。请实现一个 MyContext,其 WithValue 方法赋值的 k,v 在父节点中也可以查得到。模板:mycontext.go。
实现
1 | package main |
选做++:实现,或封装社区的 timewheel
作业内容
使用 benchmark 或 pprof 比较大量 timer 存活时,内置 timer 和 timewheel 的性能差异。不要求提交代码,只需要性能对比报告 markdown