文盘Rust -- 生命周期问题引发的 static hashmap 锁 | 京东云技术团队
2021年上半年,撸了个rust cli开发的框架,基本上把交互模式,子命令提示这些cli该有的常用功能做进去了。项目地址:https://github.com/jiashiwen/interactcli-rs。
春节以前看到axum已经0.4.x了,于是想看看能不能用rust做个服务端的框架。
春节后开始动手,在做的过程中会碰到各种有趣的问题。于是记下来想和社区的小伙伴一起分享。社区里的小伙伴大部分是DBA和运维同学,如果想进一步了解更底层的东西,代码入手是个好路数。
我个人认为想看懂代码先要写好代码,起码了解开发的基本路数和工程的一般组织模式。但好多同学的主要工作并不是专职开发,所以也就没有机会下探研发技术。代码这个事儿光看书是不管用的。了解一门语言最好的方式是使用它。
那么,问题来了非研发人员如何熟悉语言呢?咏春拳里有句拳谚:”无师无对手,桩与镜中求“。解释两句,就是在没有师兄弟练习的情况下,对着镜子和木人桩练习。在这里我觉得所谓桩有两层含义,一个是木人桩,就是练习的工具,一个是”站桩“,传统武术训练基本功的方法。其实在实际的工作中DBA和运维同学会有很多场景需要编程,比如做一些运维方面的统计工作;分析问题时需要拿到某些数据。如果追求简单用Python的话可能对于其他语言就没有涉猎了。如果结合你运维数据库的原生开发语言,假以时日慢慢就能看懂相关的底层逻辑了。我个人有个观点,产品研发的原生语言是了解产品底层最好的入口。
后面如果在Rust的开发过程中有其他问题,我本人会把问题结合实际也写到这个系列里,也希望社区里对Rust感兴趣的小伙伴一起来”盘Rust“。 言归正传,说说这次在玩儿Rust时遇到的问题吧。
在 Rust 开发过程中,我们经常需要全局变量作为公共数据的存放位置。通常做法是利用 lazy_static/onecell 和 mux/rwlock 生成一个静态的 collection。
代码长这样
use std::collections::HashMap;
use std::sync::RwLock;lazy_static::lazy_static! {static ref GLOBAL_MAP: RwLock<HashMap<String,String>> = RwLock::new({let map = HashMap::new();map});
}
基本的数据存取这样实现
use std::collections::HashMap;
use std::sync::RwLock;lazy_static::lazy_static! {static ref GLOBAL_MAP: RwLock<HashMap<String,String>> = RwLock::new({let map = HashMap::new();map});
}fn main() {for i in 0..3 {insert_global_map(i.to_string(), i.to_string())}print_global_map();println!("finished!");
}fn insert_global_map(k: String, v: String) {let mut gpw = GLOBAL_MAP.write().unwrap();gpw.insert(k, v);
}fn print_global_map() {let gpr = GLOBAL_MAP.read().unwrap();for pair in gpr.iter() {println!("{:?}", pair);}
}
insert_global_map函数用来向GLOBAL_MAP插入数据,print_global_map()用来读取数据,上面程序的运行结果如下
("0", "0")
("1", "1")
("2", "2")
下面我们来实现一个比较复杂一点儿的需求,从 GLOBAL_MAP 里取一个数,如果存在后面进行删除操作,直觉告诉我们代码似乎应该这样写
use std::collections::HashMap;
use std::sync::RwLock;lazy_static::lazy_static! {static ref GLOBAL_MAP: RwLock<HashMap<String,String>> = RwLock::new({let map = HashMap::new();map});
}fn main() {for i in 0..3 {insert_global_map(i.to_string(), i.to_string())}print_global_map();get_and_remove(1.to_string());println!("finished!");
}fn insert_global_map(k: String, v: String) {let mut gpw = GLOBAL_MAP.write().unwrap();gpw.insert(k, v);
}fn print_global_map() {let gpr = GLOBAL_MAP.read().unwrap();for pair in gpr.iter() {println!("{:?}", pair);}
}fn get_and_remove(k: String) {println!("execute get_and_remove");let gpr = GLOBAL_MAP.read().unwrap();let v = gpr.get(&*k.clone());let mut gpw = GLOBAL_MAP.write().unwrap();gpw.remove(&*k.clone());
}
上面这段代码输出长这样
("0", "0")
("1", "1")
("2", "2")
execute get_and_remove
代码没有结束,而是hang在了get_and_remove函数。 为啥会出现这样的情况呢?这也许与生命周期有关。gpr和gpw 这两个返回值分别为 RwLockReadGuard 和 RwLockWriteGuard,查看这两个
struct 发现确实可能引起死锁
must_not_suspend = "holding a RwLockWriteGuard across suspend \points can cause deadlocks, delays, \and cause Future's to not implement `Send`"
问题找到了就可以着手解决办法了,既然是与rust的生命周期有关,那是不是可以把读和写分别放在两个不同的生命周期里呢,于是对代码进行改写
use std::collections::HashMap;
use std::sync::RwLock;lazy_static::lazy_static! {static ref GLOBAL_MAP: RwLock<HashMap<String,String>> = RwLock::new({let map = HashMap::new();map});
}fn main() {for i in 0..3 {insert_global_map(i.to_string(), i.to_string())}print_global_map();get_and_remove(1);println!("finished!");
}fn insert_global_map(k: String, v: String) {let mut gpw = GLOBAL_MAP.write().unwrap();gpw.insert(k, v);
}fn print_global_map() {let gpr = GLOBAL_MAP.read().unwrap();for pair in gpr.iter() {println!("{:?}", pair);}
}fn get_and_remove_deadlock(k: String) {println!("execute get_and_remove");let gpr = GLOBAL_MAP.read().unwrap();let _v = gpr.get(&*k.clone());let mut gpw = GLOBAL_MAP.write().unwrap();gpw.remove(&*k.clone());
}fn get_and_remove(k: i32) {let v = {let gpr = GLOBAL_MAP.read().unwrap();let v = gpr.get(&*k.to_string().clone());match v {None => Err(anyhow!("")),Some(pair) => Ok(pair.to_string().clone()),}};let vstr = v.unwrap();println!("get value is {:?}", vstr.clone());let mut gpw = GLOBAL_MAP.write().unwrap();gpw.remove(&*vstr);
}
正确输出
("1", "1")
("0", "0")
("2", "2")
get value is "1"
("0", "0")
("2", "2")
finished!
Rust的生命周期是个很有意思的概念,从认识到理解确实有个过程。
源码地址
作者:京东科技 贾世闻
来源:京东云开发者社区 转载请注明来源
相关文章:
文盘Rust -- 生命周期问题引发的 static hashmap 锁 | 京东云技术团队
2021年上半年,撸了个rust cli开发的框架,基本上把交互模式,子命令提示这些cli该有的常用功能做进去了。项目地址:https://github.com/jiashiwen/interactcli-rs。 春节以前看到axum已经0.4.x了,于是想看看能不能用rus…...

SpringMVC入门篇
目录 1.SpringMVC工作流程 2.SpringMVC核心组件 2.1 DispatcherServlet 2.2 HandlerMapping 2.3 Handler 2.4 HandlerAdapter 2.5 ViewResolver 2.6 View 3.SpringMVC的入门 3.1 添加相关依赖 3.2 创建Spring-mvc.xml 3.3 配置web.xml 3.4 效果演示 4.静态资源处…...

面经:安卓学习笔记
文章目录 1. Android系统架构2. Activity2.0 定义2.1 生命周期2.2 生命状态2.3 启动模式 3. Service3.1 定义3.2 两种启动方式3.3 生命周期3.4 跨进程service3.5 IntentService 4. BroadCastReceiver4.1 概念4.2 组成4.3 广播接收器的分类4.4 生命周期4.5 静态注册和动态注册 5…...

Java设计模式:四、行为型模式-06:观察者模式
文章目录 一、定义:观察者模式二、模拟场景:观察者模式2.1 观察者模式2.2 引入依赖2.3 工程结构2.4 模拟摇号2.4.1 摇号服务接口2.4.2 摇号返回结果类 三、违背方案:观察者模式3.0 引入依赖3.1 工程结构3.2 添加摇号接口和实现3.2.1 摇号服务…...

vscode中讨厌的蓝色波浪线的去除小trick和原理
问题描述 不小心“设置同步”时和远程电脑的合并(merge)了,然后就出现了这个问题!烦死了!!! 大概是这个样子: 解决办法 站在了巨人的肩膀上,在下图位置输入这样一行参数&…...

开发工具——IDE安装 / IDEA子module依赖导入失败编译提示xx找不到符号 / IDEA在Git提交时卡顿
近期换了工作电脑,公司的IT团队不够给力,不能复制电脑系统,所以又到了需要重装IDE配置开发环境的时候了;在安装和导入Java编译器IDEA的时候遇到一些"棘手"问题,这里整理下解决方法以备不时之需; …...

AcWing 787:归并排序
【题目来源】https://www.acwing.com/problem/content/789/【题目描述】 给定你一个长度为 n 的整数数列。 请你使用归并排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。【输入格式】 输入共两行,第一行包含整数 n。 第二行包含 n 个整数&#…...
SeamlessM4T—Massively Multilingual Multimodal Machine Translation
本文是LLM系列的文章,针对《SeamlessM4T—Massively Multilingual & Multimodal Machine Translation》的翻译。 SeamlessM4T:大规模语言多模态机器翻译 摘要1 引言2 多模态翻译的社会技术维度2.12.22.3 3 SeamlessAlign:自动创建语音对…...
Python数据分析-Numpy
Numpy 个人笔记,仅供参考,谢谢 导入 import numpy import numpy as np from numpy import *Numpy数组对象 引入 # 让列表1 a [1,2,3,4],b [4,5,6,7] [x1 for x in a] # 实现ab a b > [1,2,3,4,5,6,7,8] [x y for (x,y) in zip(a,b)] -------…...

【真题解析】系统集成项目管理工程师 2023 年上半年真题卷(案例分析)
本文为系统集成项目管理工程师考试(软考) 2023 年上半年真题(全国卷),包含答案与详细解析。考试共分为两科,成绩均 ≥45 即可通过考试: 综合知识(选择题 75 道,75分)案例分析(问答题 4 道,75分)案例分析(问答题*4)试题一试题二试题三试题四案例分析(问答题*4) …...

【GAMES202】Real-Time Global Illumination(in 3D)—实时全局光照(3D空间)
一、SH for Glossy transport 1.Diffuse PRT回顾 上篇我们介绍了PRT,并以Diffuse的BRDF作为例子分析了预计算的部分,包括Lighting和Light transport,如上图所示。 包括我们还提到了SH,可以用SH的有限阶近似拟合球面函数ÿ…...

金蝶云星空二开,公有云执行SQL
功能背景; 金蝶公有云执行sql工具,因官方为云部署 用户无法连接数据库增删改查 天梯维护网页仅支持增删改操作 二开单据已支持根据sql动态生成单据体 与sql可视化界面操作一致 功能实现及场景: 1.可用于公有云执行sql类操作 2.私有云部署&am…...
JAVA String 二维的字符串数组 String[][]
String[][] 表示一个二维的字符串数组,也可以称为字符串矩阵。它是由多个一维的字符串数组组成的,每个一维数组都表示矩阵中的一行。 在 Java 中,可以使用如下方式声明和初始化一个二维字符串数组: String[][] matrix new Strin…...

【Unity3D赛车游戏优化篇】【九】Unity中如何让汽车丝滑漂移?
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:Uni…...

el-dialog设置高度、使用resetFields清除表单项无效问题
初学者容易踩坑的的el-dialog、el-form问题 1. el-dialog设置高度2. el-form中表单项对不齐3. 使用resetFields清除表单项无效 1. el-dialog设置高度 在el-dialog中里面添加一个div设置固定高度,或者限制最小的高度。 <el-dialogtitle"选择图标"v-mod…...
MySql切换到达梦数据库,各种问题解决记录
参考官方文档: https://eco.dameng.com/document/dm/zh-cn/sql-dev/practice-func.html 1. 关键字导致的报错:如ref,comment,top,domain等 Error -2007: 第 1 行, 第 117 列[ref]附近出现错误: 语法分析出错解决方案:修改关键字即可 2. 查…...

2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆
2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆...
vscode中使用eslint+prettier的配置
eslintprettiervscode自动保存用起来感觉非常爽快。 一般来说,安装eslintprettier插件,然后使用相关脚手架配套的eslintprettier,无法自动格式代码,每次都需要执行格式化命令。这里贴出保存自动格式化代码的setting.json。 // .…...

HTML 标签讲解
HTML 标签讲解 HTML 语言结构根元素元数据元素主体根元素大纲元素文本内容语义化内联文本图像与多媒体编辑标识table表格内容表单内容table表单 HTML 语言结构 Markup (标记、标签)用来容纳和描述内容 严格意义上,标签是指开始标签…...
ue5 小知识点 ue的world type,pie editor game
说明以该命令行模式启动游戏的前提下的两个问题: 1.WITH_EDITOR中的代码会被编译 2.由于没有在编辑器中(即没有打开虚幻编辑器),所以GIsEditor为false WITH_EDITOR和WITH_EDITORONLY_DATA的区别 在论坛中找到的答案: WITH_EDITORONLY_DAT…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...