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

Rust 全局变量的最佳实践 lazy_static/OnceLock/Mutex/RwLock

在实际项目开发中,难免需要用到全局变量,比如全局配置信息,全局内存池等,此类数据结构可能在多处需要被使用,保存为全局变量可以很方便的进行修改与读取。

在Rust中,如果只是读取静态变量是比较简单的,比如全局变量是一个usize或者& str等类型的值。如果全局变量是需要初始化产生的就比较复杂了,比如解析一个配置文件,然后把配置文件中的内容赋给全局变量。由于全局变量要被修改,这个全局变量得是可变的,也就是说产生了全局可变变量,而这种方式违反了Rust的设计原则。

一般方法

struct Config {
    id: u64,
}

impl Config {
    fn new() -> Config {
        Config {
            id: 0,
        }
    }
}

lazy_static::lazy_static! {
    static ref CACHE: Mutex<Config> = Mutex::new(Config::new());
}

fn func1() {
    CACHE.lock().unwrap().id = 1;
}

fn func2() {
    CACHE.lock().unwrap().id = 2;
}

fn func3() -> u64 {
    return CACHE.lock().unwrap().id;
}

fn main() {
  func1();
  let id1 = func3();
  println!("id1 = {}", id1);

  func2();
  let id2 = func3();
  println!("id2 = {}", id2);
}

这种方法一般可以满足需求,但是需要一开始就将变量初始化。
如果有未初始化的字段,可以使用Option等类型搞定。

更好的办法

use std::{
    sync::{OnceLock, RwLock},
    thread,
    time::Duration,
};

struct AppData {
    count: i64,
    name: String,
    ptr: usize,
}

static APP_SHARE: OnceLock<RwLock<AppData>> = OnceLock::new();

fn th_loop1() {
    let app = APP_SHARE.get().unwrap();
    loop {
        {
            let app2 = app.read().unwrap();
            println!("th 1 read: {}", app2.count);
            if (app2.count > 60) {
                break;
            }
        }
        thread::sleep(Duration::new(1, 0));
    }
	println!("th 1 end!!");
}

fn th_loop2() {
    let app = APP_SHARE.get().unwrap();
    loop {
        {
            let mut app2 = app.write().unwrap();
            app2.count += 1;
            println!("th 2 write: {}", app2.count);
            if (app2.count > 80) {
                break;
            }
        }
        thread::sleep(Duration::new(1, 0));
    }
	println!("th 2 end!!");
}

fn main() {
    // 在这里初始化
    let app = APP_SHARE.get_or_init(|| {
        RwLock::new(AppData {
            count: 12 * 4 + 5,
            name: "abc".to_string(),
            ptr: (0xFF002403) as usize,
        })
    });
    {
        let app2 = app.read().unwrap();
        println!("init ok: {} {} {}", app2.count, app2.name, app2.ptr);
    }
    // 线程里面使用
    let t1 = thread::spawn(th_loop1);
    let t2 = thread::spawn(th_loop2);
    t1.join().unwrap();
    t2.join().unwrap();
    println!("===============");
}

这里的 AppData 一开始没有初始化,在程序运行时才进行。
能更好适应一般的全局变量需求!!

读多写少的情况,也可以这样:

use std::{
    cell::{Cell, RefCell},
    default,
    sync::{Arc, OnceLock, RwLock},
    thread,
    time::Duration,
};
struct T1 {
    a: i64,
    b: i64,
}
struct AppData {
    count: i64,
    name: String,
    ptr: usize,
    count2: RwLock<T1>,
}

static APP_SHARE: OnceLock<AppData> = OnceLock::new();

fn th_loop1() {
    let app = APP_SHARE.get().unwrap();
    loop {
        {
            let c2 = app.count2.read().unwrap();
            println!("th 1 read: {} {}", app.count, c2.a);
            if (app.count > 60 || c2.a > 10) {
                break;
            }
        }
        thread::sleep(Duration::new(1, 0));
    }
    println!("th 1 end!!");
}

fn th_loop2() {
    let app = APP_SHARE.get().unwrap();
    loop {
        {
            let mut c2 = app.count2.write().unwrap();
            c2.a += 1;
            println!("th 2 write: {} {}", app.count, c2.a);
            if (app.count > 80 || c2.a > 20) {
                break;
            }
        }
        thread::sleep(Duration::new(1, 0));
    }
    println!("th 2 end!!");
}

fn main() {
    // 在这里初始化
    let app = APP_SHARE.get_or_init(|| AppData {
        count: 12 * 4 + 5,
        name: "abc".to_string(),
        ptr: (0xFF002403) as usize,
        count2: RwLock::new(T1 { a: 1, b: 2 }),
    });
    {
        println!("init ok: {} {} {}", app.count, app.name, app.ptr);
    }
    // 线程里面使用
    let t1 = thread::spawn(th_loop1);
    let t2 = thread::spawn(th_loop2);
    t1.join().unwrap();
    t2.join().unwrap();
    println!("===============");
}


http://www.kler.cn/a/317391.html

相关文章:

  • 【C++学习(37)】并发性模式:如生产者-消费者、读写锁等。 架构模式:如MVC、MVVM等。属于23 种设计模式吗? RAII 的关系?
  • 论文翻译 | The Capacity for Moral Self-Correction in Large Language Models
  • ios swift开发--ios远程推送通知配置
  • Thread类及常见方法
  • 数据集标注txt文件读取小工具
  • 《MYSQL45讲》误删数据怎么办
  • 02 BlockChain-- ETH
  • 着色器ShaderMask
  • HTML、CSS
  • python机器学习足球数据建模与分析——数据预测与预测建模
  • 嵌入式综合实验平台-嵌入式综合实训实验箱
  • [Excel VBA]如何使用VBA按行拆分Excel工作表
  • PHP智慧教育新篇章优校管理系统小程序源码
  • uniapp常用声明周期
  • 【C++指南】inline内联函数详解
  • network request to https://registry.npmjs.org/xxx failed, reason: connect ETIM
  • Cesium影像纠偏:高德地图加载与坐标系纠偏技巧
  • 【机器学习(九)】分类和回归任务-多层感知机 (MLP) -Sentosa_DSML社区版
  • Github 2024-09-21Rust开源项目日报 Top10
  • Qt/C++ 了解NTFS文件系统,解析0x80 $Data属性,获取Run Lists数据列表
  • 仓颉编程语言4,遇到BUG求助
  • 数据中心里全速运行的处理器正在浪费能源
  • golang格式化输入输出
  • 【3D打印】使用simplify 3D切片更改Gcode手动断电续打、掉电、未打完继续打印、补救
  • Parallels Desktop 20 for Mac 推出:完美兼容 macOS Sequoia 与 Win11 24H2
  • 【Godot4.3】自定义数列类NumList