天天看點

ios 建立全局變量static_Rust 之 全局變量的使用

全局變量

假如我們正在編寫網絡代碼,我們希望有一個全局變量,一個計數器,每次發送一個資料包都給他遞增一次:

// 伺服器成功處理的資料包數
 static PACKETS_SERVED : usize = 0;
           

這裡可以編譯通過,不過有個問題: PACKETS_SERVED是不可修改的,是以無法修改它。

Rust會盡可能阻止全局可修改狀态。當然使用const聲明的常量是不可修改的。靜态變量預設是不可修改的,是以無法取得某個值的mut引用。static可以聲明為mut,但是再通路它就是不安全的。Rust的這些規則的主要目的都是保證線程安全。(雖然可以通過unsafe代碼塊修改static的值但是不推薦)

static mut NUM: u8 = 0;
 ​
 fn main() {
   unsafe {
     NUM += 1;
   }
   unsafe {
     println!("NUM = {:?}", NUM);
   }
 }
           

支援遞增PACKETS_SERVED,同時又能保證線程安全的最簡單的方式,就是把它改成一個原子整數:

use std::sync::atomic::AtomicUsize;
 use std::sync::atomic::Ordering;
 ​
 static PACKETS_SERVED: AtomicUsize = AtomicUsize::new(0);
 ​
 fn main() {
   for _ in 0..100 {
     PACKETS_SERVED.fetch_add(1, Ordering::SeqCst);
   }
   
   println!("Packets served = {:?}", PACKETS_SERVED);
 }
           

這裡使用AtomicUsize::new(0)來初始化一個原子類型的整數。

一些方法

//Adds to the current value, returning the previous value.
 fn fetch_add(&self, val: isize, order: Ordering) -> isize;
 ​
 //Subtracts from the current value, returning the previous value.
 fn fetch_sub(&self, val: isize, order: Ordering) -> isize;
 ​
 // Bitwise "and" with the current value.
 fn fetch_and(&self, val: isize, order: Ordering) -> isize;
 ​
 ​
 // Bitwise "or" with the current value.
 fn fetch_or(&self, val: isize, order: Ordering) -> isize;
 ​
 ​
 // Bitwise "xor" with the current value.
 fn fetch_xor(&self, val: isize, order: Ordering) -> isize
 ​
 ​
 // Loads a value from the atomic integer.
 fn load(&self, order: Ordering) -> isize;
 ​
 //Stores a value into the atomic integer.
 fn store(&self, val: isize, order: Ordering);
 ​
 // Stores a value into the atomic integer if the current value is the same as the current value.
 fn compare_and_swap(
     &self,
     current: isize,
     new: isize,
     order: Ordering
 ) -> isize;
 ​
 //Stores a value into the atomic integer if the current value is the same as the current value.
 fn compare_exchange(
     &self,
     current: isize,
     new: isize,
     success: Ordering,
     failure: Ordering
 ) -> Result<isize, isize>;
 ​
           

原子全局變量隻能是簡單的整數或者布爾值。要建立其他任何類型的全局變量也需要解決兩個問題,

  • 變量必須通過某種方式保證線程安全,因為要不然就不能是全局變量。考慮到安全,靜态變量必須即是Sync, 又是非mut

    Rust有針對性的安全共享可變化值的類型:Mutex, RwLock和原子類型。這些類型即使在被聲明為非mut的情況下也是可修改的,這就是他們的目的所在。

  • 靜态初始化不能調用函數,這意味着聲明靜态Mutex的顯式方式行不通。
error: expected identifier, found keyword `ref`
   --> src/main.rs:13:8
    |
 13 | static ref HOSTNAME: Mutex<String> = Mutex::new(String::new());
    |        ^^^ expected identifier, found keyword
           

可以看到這樣是有錯誤的。

解決方法:

可以使用lazy_static包來解決這個問題。

通過lazy_static宏定義的變量可以使用任何表達式來初始化。這個表達式會在變量第一次被解引用時運作,而值會儲存下來供後續操作使用。

可以像下面這樣使用lazy_static來聲明一個全局Mutex

#[macro_use] extern crate lazy_static;
 lazy_static! {
   static ref HOSTNAME: Mutex<String> = Mutex::new(String::new());
 }
 fn main() {
   fn i in 0..10 {
     HOSRNAME.lock().unwrap().push_str(&format!("{}", i));
   }
   println!("HOSTNAME = {:?}", HOSTNAME.lock());
 }
           

同樣的技術也可以用于RwLock和AtomicPtr變量。

使用lazy_static!會在每次通路靜态資料時造成微微小的性能損失,因為其實作是用了為一次性初始化而設計的一個低級同步原語std::sync::Once,在背景每次通路懶靜态資料,程式都要執行一次原子加載指令已檢查初始化是否完成。