初学rust,多线程中读写全局变量
Tag 全局变量, 读写, rust, on by view 103

参考上一篇《初学rust,如何实现在运行时对全局变量设置和读取》,文章中说,可以用OnceCell来定义全局变量,然后就可以对全局变量进行读写,实际上,我发现OnceCellset()方法只能调用一次,如果你试图第二次调用set()来修改已经设定好的值,将会报错,设置值就失败了。

由于这一切都是为了读写的并发安全。所以OnceCell是无法实现读写全局变量的。要实现读写全局变量,应该使用RwLock。如下

定义全局变量

static WORKER_PID: RwLock<Option<u32>> = RwLock::new(None);

读写全局变量

// 写入
fn store_worker_pid(pid: u32) {
    let mut data = WORKER_PID.write().unwrap();
    *data = Some(pid);
}

// 读取
let data = WORKER_PID.read().unwrap();
let cpid = data.unwrap();

这里的读写就可以多次进行了。可以看到,这里使用了读写锁,写的时候会先通过write()方法获取写入锁实例,然后对其赋值写入。读取的时候会先通过read()方法获取读取锁实例,然后取里面的值。这样一来就实现了全局变量的读写。


初学rust,如何实现在运行时对全局变量设置和读取
Tag 运行时, 全局变量, 读写, on by view 241

在rust中,如何实现在运行时对全局变量设置和读取,这个问题困扰了我一段时间。因为在rust中,全局变量是不能在运行时修改的。rust的全局变量是属于全局静态变量,使用关键词static来定义,如下:

static mut count: usize = 0;

pub fn init_config(file_path: &str) {
    count = count + 1;
}

编译器会告诉你修改count不安全(unsafe)。

➜  las git:(feature/self-update-with-watcher-fork) ✗ cargo build
   Compiling las v0.1.1 (/root/code/las)
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/config/cfg.rs:35:13
   |
35 |     count = count + 1;
   |             ^^^^^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/config/cfg.rs:35:5
   |
35 |     count = count + 1;
   |     ^^^^^^^^^^^^^^^^^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

For more information about this error, try `rustc --explain E0133`.
error: could not compile `las` (bin "las") due to 2 previous errors

一定要修改的话,需要加unsafe块包裹住:

static mut count: usize = 0;

pub fn init_config(file_path: &str) {
    unsafe { count = count + 1 };
}

但是,在rust中使用unsafe是一件不好的事情,我们应该尽量避免使用unsafe,这里有另一个方案可以实现在运行时设置全局变量,就是使用once_cell这个包,虽然它的底层也是基于unsafe来实现的,但是它对“不安全”代码进行了一定的包装。具体用法如下:

static CFG: OnceCell<Conf> = OnceCell::new();

// 初始化和设置
pub fn init_config(file_path: &str) {
    CFG.get_or_init(|| load_config(file_path));
}

// 读取
pub fn get_config() -> &'static Conf {
    CFG.get().unwrap()
}

可以看到,我们可以在运行时使用once_cellget_or_init方法对全局变量进行初始化设置,它还有set方法也可以在运行时对全局变量进行设置,get方法在运行时对全局变量进行读取。从而实现不直接使用unsafe块,来操作全局变量。而且once_cell使用的全局变量完全不必申明为可变(mut)变量。

我们可以通过追踪once_cell::get_or_init方法,可以看到在initialize方法中unsafe块中,将value设置给了*slot

qby9tqm9

可以看到,once_cell对全局变量的操作进行了安全封装,因此,建议使用once_cell进行全局变量的操作,而不是到处使用unsafe块进行不安全的操作。

^注意:实践证明,OnceCell只能调用set()对全局变量设置一次,后续再调用set()就会报错。要反复读写甚至多线程中读写全局变量,应该使用RwLock,详情参考新的文章《初学rust,多线程中读写全局变量》