一道Gloang并发、锁的面试题,你会吗?

2023-05-16

Gloang并发、锁的面试题

  • 1.题目描述
  • 2.问题分析
    • 2.1问题一
    • 2.2问题二
    • 2.3问题三
    • 2.4问题四
    • 2.5问题五
  • 3.问题解决方法
  • 4.代码实现
    • 4.1 map前后加锁的方式
    • 4.2 sync.map解决方式

1.题目描述

源地址:一道并发和锁的golang面试题

场景:
在一个高并发的web服务器中,要限制IP的频繁访问。
现模拟100个IP同时并发访问服务器,每个IP要重复访问1000次。
每个IP三分钟之内只能访问一次。
修改以下代码完成该过程,要求能成功输出 success:100

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)
}

2.问题分析

如果直接运行上述代码的话,会报错。

2.1问题一

变量j,表示的ip地址的最后一位,被多个协程使用,然而并不是以值的方式传入的。这种方式相当于拿着j的地址运行。看下面代码:

for j := 0; j < 10; j++ {
	go func() {
		fmt.Println(j)
	}()
}

打印结果,肯定不是0 1 2 3 4 …9

2.2问题二

变量success,被多个协程同时读写,且并未加锁

2.3问题三

visit函数中的map,也不是协程安全的

2.4问题四

启动了多个协程,并未等待这些协程,就有可能退出主main

2.5问题五

并未判断是否超时3s,假设时间超过了3s,success也不会加一

3.问题解决方法

① 给闭包添加形参,将j作为实参传入
② success前后加锁,或者原子操作
③ map操作前后加锁,或者sync.map
④ 使用sync.WaitGroup对协程进行阻塞控制
⑤ 添加时间判断,超过3s的,success++,并重新更新时间

4.代码实现

go run -race *.go
检测是否存在数据读写竞争

4.1 map前后加锁的方式

visit这样写可能不是最优,但是最容易理解

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type Ban struct {
	visitIPs map[string]time.Time
}

var mx sync.Mutex

func NewBan() *Ban {
	return &Ban{visitIPs: make(map[string]time.Time)}
}
func (o *Ban) visit(ip string) bool {  
	mx.Lock()
	defer mx.Unlock()
	if t, ok := o.visitIPs[ip]; ok { //查看该ip是否已经访问过
		if time.Now().Before(t.Add(3 * time.Second)) { //时间检测,查看当前时间是否在计时开始的3s后
			//true 代表尚未到达3s
			return true
		} else {
			//false 代表已经超过3s,则可以访问,并更新时间
			o.visitIPs[ip] = time.Now()
			return false
		}
	}
	o.visitIPs[ip] = time.Now()
	return false
}

func main() {
	var success int64
	var wg sync.WaitGroup
	ban := NewBan()
	for i := 0; i < 1000; i++ {
		for j := 0; j < 100; j++ {
			wg.Add(1)
			go func(temp int) {
				ip := fmt.Sprintf("192.168.1.%d", temp)
				if !ban.visit(ip) {
					atomic.AddInt64(&success, 1)
				}
				wg.Done()
			}(j)
		}

	}
	wg.Wait()
	fmt.Println("success:", success)
}

4.2 sync.map解决方式

只需要更改以下函数

type Ban struct {
	visitIPs sync.Map
}

func NewBan() *Ban {
	return &Ban{visitIPs: sync.Map{}}
}

func (o *Ban) visit(ip string) bool {
	mx.Lock()
	defer mx.Unlock()
	if v, ok := o.visitIPs.Load(ip); ok {
		t := v.(time.Time)
		if time.Now().Before(t.Add(3 * time.Second)) { //时间检测,查看当前时间是否在计时开始的3s后
			//true 代表尚未到达3s
			return true
		} else {
			//false 代表已经超过3s,则可以访问,并更新时间
			o.visitIPs.Store(ip, time.Now())
			return false
		}
	}
	o.visitIPs.Store(ip, time.Now())
	return false
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一道Gloang并发、锁的面试题,你会吗? 的相关文章

随机推荐

  • 面试可能遇到的问题野指针等解决方法

    空指针 xff1a 一般声明一个指针变量赋值为NULL xff0c 这就是空指针 xff0c 各个类型的空指针都存在确确实实的内存地址 xff0c 但是不会指向任何有效的值的内存地址 xff0c 对空指针操作 xff0c 例如访问属性和方法
  • 大规模分布式储存系统笔记(一)

    分布式储存系统的特性 xff1a 1 可扩展性 可按集群规模增长 xff0c 系统性能线性增长 xff1b 2 低成本 系统自动容错 xff0c 自动负载均衡 xff0c 运维方便 3 高性能 4 易用性 对外提供接口 数据类型 xff1a
  • 用MATLAB实现对运动物体识别与跟踪

    不得不说MATLAB的图像处理函数有点多 xff0c 但速度有时也是出奇的慢 还是想c的指针 xff0c 虽然有点危险 xff0c 但速度那是杠杠的 第二个MATLAB程序 xff0c 对运动物体的识别与追踪 这里我们主要运用帧差法实现运动
  • PS 开启GPU加速图片处理

    还认为你的电脑的速度效果比不上苹果吗 xff1f 还在嫌电脑渲染速度慢吗 xff1f 试一下 xff0c 电脑开启GPU硬件加速吧 xff01 只要有独显轻松加速 xff08 毕竟苹果笔记本要配独显电脑的价格基本上在15000以上 xff0
  • 管道鸟cortex-M4(TM4C1294)

    看到满屏的贪吃蛇 xff0c 我也来开源一个Ti开发板 xff08 TM4C1294 xff09 的游戏 将简化版的管道鸟 xff0c 根据自己玩的经历 xff0c 在cortexm4开发板上重新撸了一边 xff0c 设计思路 xff1a
  • C#连接MYSQL数据库并进行查询

    之前用MFC开发结果界面太难看被pass了 要求用C 重新来开发 gt lt 不过终于摆脱VC6 0的蛋疼操作了Y 先来连接数据库 xff08 1 xff09 用c 连接MYSQL数据库需要用到mysql connector net xff
  • binascii.Error: Incorrect padding 报错解决

    输入的base64编码字符串必须符合base64的padding规则 当原数据长度不是3的整数倍时 如果最后剩下两个输入数据 xff0c 在编码结果后加1个 61 xff1b 如果最后剩下一个输入数据 xff0c 编码结果后加2个 61 x
  • 通过过滤器链了解spring security + oauth2实现单点登录的过程

    一 系统 注意部署在同一机器 xff08 localhost xff09 上的三个应用 xff0c 为了防止存放在cookie中的JSESSIONID不被覆盖 xff0c 需要设置不同的path xff0c 可以在配置文件中指定不同的上下文
  • jetson tx2开箱上电

    期待已久的jetson tx2终于到了 xff0c 来做一个开箱 jetson tx2是英伟达的第三代GPU嵌入式开发板 前两代分别是jetson tk1和jetson tx1 jetson tk1 xff1a 绿色的版板子接口丰富 jet
  • Jetson tx2刷机过程中的坑

    暑假各种事忙得差不多后 xff0c 终于有时间拿出早就申请到的tx2 xff0c 开始刷机教程 xff0c 这两天几乎踩边了所有的坑 第一个坑 xff0c 虚拟机 一般在安装VMware虚拟机时 xff0c 建议的安装空间20GB xff0
  • python词云实现

    python的一个蛮酷炫的功能是可以轻松地实现词云 github上有关于这个项目的开源代码 xff1a https github com amueller word cloud 注意跑例程时要删除里面的wordcloud文件夹 词云的功能有
  • docker中accessTokens拉取私有git仓库

    背景 当需要git clone拉取私有库时 xff0c 传统的做法为将本机的ssh配置到gitlab中 但在docker中执行程序时需要拉取私有库 xff0c 此时无法为每个docker容器配置ssh 网上的一种方案为 xff0c 将配置好
  • Docker世界 -- 进阶篇(入门)

    一 Docker Compose 1 1 Docker Compose 介绍 1 1 1 简介 xff1a 传统的 docker 服务 xff0c 我们一般通过编写 Dockerfile 文件 xff0c 通过 build 命令创建一个镜像
  • 树莓派pico CMake工程 直接添加 .c .h文件

    假设工程名test1 xff0c 带main 的源代码文件 main c xff0c 要往工程里添加oled c oled h之类的源代码 直接添加为可执行文件 xff1a 编辑工程根目录的 CmakeLists txt add execu
  • 张量的通俗理解

    1 关于张量的四种定义 张量 在不同的运用场景下有不同的定义 xff08 1 xff09 张量是多维数组 xff0c 这个定义常见于各种人工智能软件 听起来还好理解 xff08 2 xff09 张量是某种几何对象 xff0c 不会随着坐标系
  • 如何搭建node_exporter

    如何搭建node exporter 1 观看条件 1 假设你已经看过上一篇文章 如何搭建普罗米修斯 Prometheus 2 假设你已经会搭建普罗米修斯 xff08 promethus xff09 3 上面两个假设 xff0c 只要满足一个
  • python类中初始化形式:def __init__(self)和def __init__(self, 参数1,参数2,···,参数n)区别

    前言 这两种初始化形式 xff0c 就类似于C 43 43 类中的构造函数 形式1 def init self span class token keyword class span span class token class name
  • Go语言操作grpc详细使用

    Go语言操作grpc详细使用 零 参考链接一 protobuf的详细使用二 grpc与protobuf的go文件的生成1 安装两个插件2 写proto文件3 编译proto文件 xff0c 生成go文件 三 grpc的详细使用1 一元RPC
  • Steghide使用教程及其密码爆破

    Steghide使用教程及其密码爆破 工具介绍 Steghide是一款开源的隐写术软件 xff0c 它可以让你在一张图片或者音频文件中隐藏你的秘密信息 xff0c 而且你不会注意到图片或音频文件发生了任何的改变 而且 xff0c 你的秘密文
  • 一道Gloang并发、锁的面试题,你会吗?

    Gloang并发 锁的面试题 1 题目描述2 问题分析2 1问题一2 2问题二2 3问题三2 4问题四2 5问题五 3 问题解决方法4 代码实现4 1 map前后加锁的方式4 2 sync map解决方式 1 题目描述 源地址 xff1a