並行
理論上並行和語言並沒有什麼關係,所以在理論上的並行方式,都可以嘗試用Rust來實現。本小節不會詳細全面地介紹具體的並行理論知識,只介紹用Rust如何來實現相關的並行模式。
Rust的一大特點是,可以保證“線程安全”。而且,沒有性能損失。更有意思的是,Rust編譯器實際上只有Send
Sync
等基本抽象,而對“線程” “鎖” “同步” 等基本的並行相關的概念一無所知,這些概念都是由庫實現的。這意味著Rust實現並行編程可以有比較好的擴展性,可以很輕鬆地用庫來支持那些常見的並行編程模式。
下面,我們以一個例子來演示一下,Rust如何將線程安全/執行高效/使用簡單結合起來的。
在圖形編程中,我們經常要處理歸一化的問題: 即把一個範圍內的值,轉換到範圍1內的值。比如把一個顏色值255歸一後就是1。假設我們有一個表示顏色值的數組要進行歸一,用非並行化的方式來處理非常簡單,可以自行嘗試。下面我們將採用並行化的方式來處理,把數組中的值同時分開給多個線程一起並行歸一化處理。
extern crate rayon;
use rayon::prelude::*;
fn main() {
let mut colors = [-20.0f32, 0.0, 20.0, 40.0,
80.0, 100.0, 150.0, 180.0, 200.0, 250.0, 300.0];
println!("original: {:?}", &colors);
colors.par_iter_mut().for_each(|color| {
let c : f32 = if *color < 0.0 {
0.0
} else if *color > 255.0 {
255.0
} else {
*color
};
*color = c / 255.0;
});
println!("transformed: {:?}", &colors);
}
運行結果:
original: [-20, 0, 20, 40, 80, 100, 150, 180, 200, 250, 300]
transformed: [0, 0, 0.078431375, 0.15686275, 0.3137255, 0.39215687, 0.5882353, 0.7058824, 0.78431374, 0.98039216, 1]
以上代碼是不是很簡單。調用par_iter_mut
獲得一個並行執行的具有寫權限的迭代器,for_each
對每個元素執行一個操作。僅此而已。
我們能這麼輕鬆地完成這個任務,原因是我們引入了 rayon 這個庫。它把所有的髒活累活都幹完了,把清晰安全易用的接口暴露出來給了我們。Rust還可以完全以庫的形式,實現異步IO、協程等更加高階的並行程序開發模式。
為了更深入的加深對Rust併發編程的理解和實踐,還安排了一個挑戰任務:實現一個Rust版本的MapReduce模式。值得你挑戰。