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
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- argparse 是python自带的命令行参数解析包,可以用来方便地读取命令行参数。一、传入一个参数import argpars
- 前言本文提供在在无音频的视频中添加音频的python工具,附上代码。环境依赖ffmpeg环境安装,可以参考:windows ffmpeg安装
- Python heapq 详解Python有一个内置的模块,heapq标准的封装了最小堆的算法实现。下面看两个不错的应用。小顶堆
- 为什么越来越多的非程序员白领都开始学习 Python ?他们可能并不是想要学习 Python 去爬取一些网站从而获得酷酷的成就感,而是工作中
- SQL Server 2005开始支持XML数据类型,提供原生的XML数据类型、XML索引及各种管理或输出XML格式的函数。随着JSON的流
- 代码片段是开发者每天都要面对的东西,甚至有时候查找代码片段的时间比编写新代码的时间还要多。因为如果找到能够在项目中直接使用的代码片段,这意味
- 导语:你不知道Python也能去除“背景”嘛?修饰图片中的头发是设计师最烦人的任务之一!要修得完美,
- web开发中避免不了运行环境的搭建,个人认为这是没有什么技术含量而又浪费时间的工作.所以将环境搭建的步骤记录下来,希望可以帮到有需要的朋友少
- 1.进入官网https://www.python.org/,点击Downloads下的Windows按钮,进入下载页面。2.如下图所示,点击
- string iconv ( string $in_charset , string $out_charset , string $str
- 在我们使用一些数据的过程中,我们想要打乱数组内数据的顺序但不改变数据本身,可以通过改变索引值来实现,也就是将索引值重新随机排列,然后生成新的
- 1、ComboBox的基础属性# -*- encoding=utf-8 -*-import tkinterfrom tkinter impo
- 本文实例讲述了Python列表切片操作。分享给大家供大家参考,具体如下:切片指的是列表的一部分。1 基本用法指定第一个元素和最后一个元素的索
- 当然考虑到浏览器(特别指IE)糟糕的js执行能力,动画效果又要受到影响。 浏览器中的动画效果主要依靠js来动态改变Dom元素外观来形成。不过
- 类的定义假如要定义一个类 Point,表示二维的坐标点:# point.pyclass Point: def __init__(
- #coding:utf-8from wsgiref.simple_server import make_serverdef RunServe
- python的日志管理模块可以用自带的logging模块,也可以用第三方的Loguru模块,关于logging和loguru模块的简单使用可
- golang用来序列化的模块有很多,我们来介绍3个。json首先登场的是json,这个几乎毋庸置疑。序列化package mainimpor
- 可切片使用Python 的切片语法来限制查询集记录的数目 。它等同于SQL 的LIMIT 和OFFSET 子句。>>> E
- 模型的恢复对于的模型的恢复来说,需要首先恢复模型的整个图文件,之后从图文件中读取相应的节点信息。存储的模型文件包括四个子文件,如下:&nbs