当前位置: 首页 > news >正文

文盘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年上半年&#xff0c;撸了个rust cli开发的框架&#xff0c;基本上把交互模式&#xff0c;子命令提示这些cli该有的常用功能做进去了。项目地址&#xff1a;https://github.com/jiashiwen/interactcli-rs。 春节以前看到axum已经0.4.x了&#xff0c;于是想看看能不能用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:观察者模式

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

vscode中讨厌的蓝色波浪线的去除小trick和原理

问题描述 不小心“设置同步”时和远程电脑的合并&#xff08;merge&#xff09;了&#xff0c;然后就出现了这个问题&#xff01;烦死了&#xff01;&#xff01;&#xff01; 大概是这个样子&#xff1a; 解决办法 站在了巨人的肩膀上&#xff0c;在下图位置输入这样一行参数&…...

开发工具——IDE安装 / IDEA子module依赖导入失败编译提示xx找不到符号 / IDEA在Git提交时卡顿

近期换了工作电脑&#xff0c;公司的IT团队不够给力&#xff0c;不能复制电脑系统&#xff0c;所以又到了需要重装IDE配置开发环境的时候了&#xff1b;在安装和导入Java编译器IDEA的时候遇到一些"棘手"问题&#xff0c;这里整理下解决方法以备不时之需&#xff1b; …...

AcWing 787:归并排序

【题目来源】https://www.acwing.com/problem/content/789/【题目描述】 给定你一个长度为 n 的整数数列。 请你使用归并排序对这个数列按照从小到大进行排序。 并将排好序的数列按顺序输出。【输入格式】 输入共两行&#xff0c;第一行包含整数 n。 第二行包含 n 个整数&#…...

SeamlessM4T—Massively Multilingual Multimodal Machine Translation

本文是LLM系列的文章&#xff0c;针对《SeamlessM4T—Massively Multilingual & Multimodal Machine Translation》的翻译。 SeamlessM4T&#xff1a;大规模语言多模态机器翻译 摘要1 引言2 多模态翻译的社会技术维度2.12.22.3 3 SeamlessAlign&#xff1a;自动创建语音对…...

Python数据分析-Numpy

Numpy 个人笔记&#xff0c;仅供参考&#xff0c;谢谢 导入 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&#xff0c;并以Diffuse的BRDF作为例子分析了预计算的部分&#xff0c;包括Lighting和Light transport&#xff0c;如上图所示。 包括我们还提到了SH&#xff0c;可以用SH的有限阶近似拟合球面函数&#xff…...

金蝶云星空二开,公有云执行SQL

功能背景&#xff1b; 金蝶公有云执行sql工具&#xff0c;因官方为云部署 用户无法连接数据库增删改查 天梯维护网页仅支持增删改操作 二开单据已支持根据sql动态生成单据体 与sql可视化界面操作一致 功能实现及场景&#xff1a; 1.可用于公有云执行sql类操作 2.私有云部署&am…...

JAVA String 二维的字符串数组 String[][]

String[][] 表示一个二维的字符串数组&#xff0c;也可以称为字符串矩阵。它是由多个一维的字符串数组组成的&#xff0c;每个一维数组都表示矩阵中的一行。 在 Java 中&#xff0c;可以使用如下方式声明和初始化一个二维字符串数组&#xff1a; String[][] matrix new Strin…...

【Unity3D赛车游戏优化篇】【九】Unity中如何让汽车丝滑漂移?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

el-dialog设置高度、使用resetFields清除表单项无效问题

初学者容易踩坑的的el-dialog、el-form问题 1. el-dialog设置高度2. el-form中表单项对不齐3. 使用resetFields清除表单项无效 1. el-dialog设置高度 在el-dialog中里面添加一个div设置固定高度&#xff0c;或者限制最小的高度。 <el-dialogtitle"选择图标"v-mod…...

MySql切换到达梦数据库,各种问题解决记录

参考官方文档&#xff1a; https://eco.dameng.com/document/dm/zh-cn/sql-dev/practice-func.html 1. 关键字导致的报错&#xff1a;如ref,comment,top,domain等 Error -2007: 第 1 行, 第 117 列[ref]附近出现错误: 语法分析出错解决方案&#xff1a;修改关键字即可 2. 查…...

2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆

2023开学礼山东财经大学《乡村振兴战略下传统村落文化旅游设计》许少辉新财经图书馆...

vscode中使用eslint+prettier的配置

eslintprettiervscode自动保存用起来感觉非常爽快。 一般来说&#xff0c;安装eslintprettier插件&#xff0c;然后使用相关脚手架配套的eslintprettier&#xff0c;无法自动格式代码&#xff0c;每次都需要执行格式化命令。这里贴出保存自动格式化代码的setting.json。 // .…...

HTML 标签讲解

HTML 标签讲解 HTML 语言结构根元素元数据元素主体根元素大纲元素文本内容语义化内联文本图像与多媒体编辑标识table表格内容表单内容table表单 HTML 语言结构 Markup &#xff08;标记、标签&#xff09;用来容纳和描述内容 严格意义上&#xff0c;标签是指开始标签&#xf…...

ue5 小知识点 ue的world type,pie editor game

说明以该命令行模式启动游戏的前提下的两个问题&#xff1a; 1.WITH_EDITOR中的代码会被编译 2.由于没有在编辑器中(即没有打开虚幻编辑器)&#xff0c;所以GIsEditor为false WITH_EDITOR和WITH_EDITORONLY_DATA的区别 在论坛中找到的答案&#xff1a; WITH_EDITORONLY_DAT…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...