切换导航
{{systemName}}
{{ info.Title }}
{{info.Title}}
{{ menu.Title }}
{{menu.Title}}
登录
|
退出
搜索
高并发下的锁与map的读写
作者:ych
场景:在一个高并发的web服务器中,要限制IP的频繁访问。现模拟100个IP同时并发访问服务器,每个IP要重复访问1000次。 每个IP三分钟之内只能访问一次。修改以下代码完成该过程,要求能成功输出 success:100 ```go package main import ( "fmt" "time" ) type Ban struct { visitIPs map[string]time.Time } func NewBan() *Ban { return &Ban{visitIPs: make(map[string]time.Time)} } func (o *Ban) visit(ip string) bool { if _, ok := o.visitIPs[ip]; ok { return true } o.visitIPs[ip] = time.Now() return false } func main() { success := 0 ban := NewBan() for i := 0; i < 1000; i++ { for j := 0; j < 100; j++ { go func() { ip := fmt.Sprintf("192.168.1.%d", j) if !ban.visit(ip) { success++ } }() } } fmt.Println("success:", success) } ``` **解析** 该问题主要考察了并发情况下map的读写问题,而给出的初始代码,又存在`for`循环中启动`goroutine`时变量使用问题以及`goroutine`执行滞后问题。 因此,首先要保证启动的`goroutine`得到的参数是正确的,然后保证`map`的并发读写,最后保证三分钟只能访问一次。 多CPU核心下修改`int`的值极端情况下会存在不同步情况,因此需要原子性的修改int值。 下面给出的实例代码,是启动了一个协程每分钟检查一下`map`中的过期`ip`,`for`启动协程时传参。 ```go package main import ( "context" "fmt" "sync" "sync/atomic" "time" ) type Ban struct { visitIPs map[string]time.Time lock sync.Mutex } func NewBan(ctx context.Context) *Ban { o := &Ban{visitIPs: make(map[string]time.Time)} go func() { timer := time.NewTimer(time.Minute * 1) for { select { case <-timer.C: o.lock.Lock() for k, v := range o.visitIPs { if time.Now().Sub(v) >= time.Minute*1 { delete(o.visitIPs, k) } } o.lock.Unlock() timer.Reset(time.Minute * 1) case <-ctx.Done(): return } } }() return o } func (o *Ban) visit(ip string) bool { o.lock.Lock() defer o.lock.Unlock() if _, ok := o.visitIPs[ip]; ok { return true } o.visitIPs[ip] = time.Now() return false } func main() { success := int64(0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ban := NewBan(ctx) wait := &sync.WaitGroup{} wait.Add(1000 * 100) for i := 0; i < 1000; i++ { for j := 0; j < 100; j++ { go func(j int) { defer wait.Done() ip := fmt.Sprintf("192.168.1.%d", j) if !ban.visit(ip) { atomic.AddInt64(&success, 1) } }(j) } } wait.Wait() fmt.Println("success:", success) } ```
相关推荐
golang 中解析 tag 是怎么实现的?反射原理是什么?(中高级肯定会问,比较难,需要自己多去总结)
使用gorm不当出现too Many Connections的问题
golang map 使用注意的点,是否并发安全?
uint 类型溢出问题
Golang中defer和return执行的先后顺序
golang orm框架 gorm
讲讲 Go 的 select 底层数据结构和一些特性?(难点,没有项目经常可能说不清,面试一般会问你项目中怎么使用select)
golang中两个变量值的4种交换方式
golang进行封包和拆包的完整解决方案
对已经关闭的 chan 进行读写,会怎么样?为什么?
调用函数传入结构体时,应该传值还是指针? (Golang 都是传值)
为 sync.WaitGroup 中Wait函数支持 WaitTimeout 功能.
单例模式的应用场景
Golang判断slice是否相等
讲讲 Go 的 defer 底层数据结构和一些特性?
Golang空结构体 struct{} 的使用
go defer,多个 defer 的顺序,defer 在什么时机会修改返回值?
序列化协议
Golang 单引号,双引号,反引号的区别?
Golang表示枚举类型的详细讲解
昨天那个在for循环里append元素的同事,今天还在么?
什么是死锁?死锁产生的原因?如何避免死锁?
golang并发题目测试
交替打印数字和字母
golang面试题
for range 的时候它的地址会发生变化么?
B+树为什么快
数组和切片的区别 (基本必问)
实现阻塞读且并发安全的map
机器人坐标问题
复利计算 递归/非递归
在 golang 协程和channel配合使用
MySQL索引原理
写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出?
讲讲 Go 的 slice 底层数据结构和一些特性?
Redis的优点
判断两个给定的字符串排序后是否一致
常见语法题目2
字符串替换问题
ElasticSearch使用场景
常见语法题目1
golang 中 make 和 new 的区别?(基本必问)
七道语法找错题目
判断字符串中字符是否全都不同
golang 实现一个负载均衡案例(随机,轮训)
redis缓存穿透、缓存击穿、缓存雪崩原因+解决方案
基本数据结构和算法
消息队列使用的场景介绍
操作系统基本原理
翻转字符串
redis在项目中的使用
TiDB使用场景
评论区
先去登录
版权所有:机遇屋在线 Copyright © 2021-2025 jiyuwu Co., Ltd.
鲁ICP备16042261号-1