golang使用map支持高并发的方法(1000万次操作14ms)
作者:飞鸟真人 发布时间:2024-05-13 10:41:33
标签:golang,map,并发
语言原生的map存在2个问题:
1)不是线程安全的;
2)数据量大时候需要尽量避免使用string等,GC压力很大;
有人使用泛型实现了相关的cocurent-map,(https://github.com/orcaman/concurrent-map)但是关于键值部分仍然默认使用了string,为了提高效率,这里对其做了一些修改,让键值也可以自定义类型:https://github.com/robinfoxnan/go_concurrent_map
基本使用方法:
// Create a new map.
m := cache.NewConcurrentMap[uint64, string]()
// Sets item within map, sets "bar" under key "foo"
m.Set(199010212, "bar")
// Retrieve item from map.
bar, ok := m.Get(199010212)
fmt.Println(bar, ok)
// Removes item under key "foo"
m.Remove(199010212)
为了实现计数器等,需要在加锁期间更新,需要使用回调函数:
// 计数器
type BaseCounter struct {
Count uint64
CountLast uint64
}
var MapOfAppUserCount ConcurrentMap[uint64, *AppUserCounter]
func InitMaps() {
MapOfAppVistedCount = NewConcurrentMap[uint64, *BaseCounter]()
}
// 没有值,则设置;如果有,则更新; 新增的部分通过新的值传递过来!
func appAddCallBack(exist bool, valueInMap *BaseCounter, newValue *BaseCounter) *BaseCounter {
if exist == false {
return newValue
} else {
valueInMap.Count += newValue.Count
return valueInMap
}
}
// 对应用计数器加i
func AppAddBy(key uint64, i uint64) uint64 {
c := BaseCounter{i, i}
res := MapOfAppVistedCount.Upsert(key, &c, appAddCallBack)
if res != nil {
return res.Count
}
return 0
}
计数器的使用如下:
cache.InitMaps()
cache.AppAddBy(i, 1)
性能:
1)单线程初始化1~1000w的计数器,2412 ms
2)分给100个协程,14ms
测试代码如下:
func testSingle() {
cache.InitMaps()
timeUnixNano1 := time.Now().UnixMilli()
// 100万次更新
for i := uint64(0); i < 10000000; i++ {
cache.AppAddBy(i, 1)
}
timeUnixNano2 := time.Now().UnixMilli()
delta := timeUnixNano2 - timeUnixNano1
fmt.Println("cost: ", delta, " ms")
count := cache.AppAddBy(1, 1)
fmt.Println(count)
count = cache.AppAddBy(1, 2)
fmt.Println(count)
count = cache.AppAddBy(1, 3)
fmt.Println(count)
}
var N int = 10000000
func doInsert(n int, index int, g *sync.WaitGroup) {
m := N / n
start := index * m
//fmt.Println("thread ", index, "from ", start)
for i := uint64(start); i < uint64(m); i++ {
cache.AppAddBy(i, 1)
}
if g != nil {
g.Done()
}
}
func testMulti() {
cache.InitMaps()
group := sync.WaitGroup{}
n := 100
group.Add(n)
timeUnixNano1 := time.Now().UnixMilli()
for i := 0; i < n; i++ {
go doInsert(n, i, &group)
}
group.Wait()
timeUnixNano2 := time.Now().UnixMilli()
delta := timeUnixNano2 - timeUnixNano1
fmt.Println("cost: ", delta, " ms")
}
来源:https://blog.csdn.net/robinfoxnan/article/details/127842693
0
投稿
猜你喜欢
- <!--#include file="config.asp" -->&nbs
- 1 项目背景1.1Python的优势Python有成熟的程序包资源库和活跃的社区 Python以PYPI为技术支撑,资源丰富,可被灵活调用。
- 1 作用通过 flask-script 可以以命令的形式,执行 flask 语句。提供向 Flask 插入外部脚本的功能,如 运行一个开发服
- web2.0教程4:如何调用css样式表 这些技巧主要讲meta标签设置的,其实与符合web标准关系不大,只要注意在最后加"/&q
- 过程名:catch(str) 使用方法: 代码如下:on error resume next '你的代码,如数据库连接 call c
- Node被设计用来高效的处理I/O操作,但是你应该知道,有些类型的程序并不适合这种模式。比如,如果你打算用Node处理一个CPU密集的任务,
- 一 位置传递没什么好过多讲解.# 位置传递实例:def fun1(a,b,c): return a+b+cprint(fun1(
- 本文实例讲述了Python实现向服务器请求压缩数据及解压缩数据的方法。分享给大家供大家参考,具体如下:向服务器请求压缩数据格式,并解压缩数据
- 下面来先看一个简单例子<!DOCTYPE html><html lang="zh-CN" ng-app
- 如果网页处于焦点状态返回true,否则返回fasle 什么是焦点?焦点是指用户是否活动在该页面.确切的说鼠标是否在该网页内活动.或者说该网页
- cmp()方法返回两个数的差的符号: -1 如果 x < y, 0 如果 x == y, 或者 1 如果 x > y
- 最近产品妹子提出了一个体验issue —— 用 iOS 在手Q阅读书友交流区发表书评时,光标点击总是不好定位到正确的位置:如上图,具体表现是
- 有时候我们在前端开发工作中为了获取图片的信息,需要在图片加载完成后才可以正确的获取到图片的大小尺寸,并且执行相应的回调函数使图片产生某种显示
- 目录结构:contents structure [-]在开始文章之前,先贴上一张Iterable、Iterator与Generator之间的
- 今天使用os.path.isdir()判断是否是文件夹的时候发现一个问题:lst = os.listdir(path) &nb
- 今天来说说xml那些事儿.如何批量修改指定文件夹下的xml文件的指定属性.分三步走,首先,我们先看看如何读写单个的xml文件;第二步,来看看
- 1. ORACLE 的解析器按照从右到左的顺序处理 FROM 子句中的表名,因此 FROM 子句中写在最后的表(基础表 driving ta
- 为了确保字符串按预期显示,我们可以使用 format() 方法对结果进行格式化。字符串 format()format() 方法允许您格式化字
- 开启mysql的远程访问权限默认mysql的用户是没有远程访问的权限的,因此当程序跟数据库不在同一台服务器上时,我们需要开启mysql的远程
- 一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。而使用@staticmethod或@classmethod,就可以不需要实例化