国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Rust編程語言入門之無畏并發(fā)

這篇具有很好參考價值的文章主要介紹了Rust編程語言入門之無畏并發(fā)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

無畏并發(fā)

并發(fā)

  • Concurrent:程序的不同部分之間獨立的執(zhí)行(并發(fā))
  • Parallel:程序的不同部分同時運行(并行)
  • Rust無畏并發(fā):允許你編寫沒有細微Bug的代碼,并在不引入新Bug的情況下易于重構(gòu)
  • 注意:本文中的”并發(fā)“泛指 concurrent 和 parallel

一、使用線程同時運行代碼(多線程)

進程與線程

  • 在大部分OS里,代碼運行在進程(process)中,OS同時管理多個進程。
  • 在你的程序里,各獨立部分可以同時運行,運行這些獨立部分的就是線程(thread)
  • 多線程運行:
    • 提升性能表現(xiàn)
    • 增加復雜性:無法保障各線程的執(zhí)行順序

多線程可導致的問題

  • 競爭狀態(tài),線程以不一致的順序訪問數(shù)據(jù)或資源
  • 死鎖,兩個線程彼此等待對方使用完所持有的資源,線程無法繼續(xù)
  • 只在某些情況下發(fā)生的 Bug,很難可靠地復制現(xiàn)象和修復

實現(xiàn)線程的方式

  • 通過調(diào)用OS的API來創(chuàng)建線程:1:1模型
    • 需要較小的運行時
  • 語言自己實現(xiàn)的線程(綠色線程):M:N模型
    • 需要更大的運行時
  • Rust:需要權(quán)衡運行時的支持
  • Rust標準庫僅提供1:1模型的線程

通過 spawn 創(chuàng)建新線程

  • 通過 thread::spawn 函數(shù)可以創(chuàng)建新線程:
    • 參數(shù):一個閉包(在新線程里運行的代碼)
? cd rust

~/rust
? cargo new thread_demo
     Created binary (application) `thread_demo` package

~/rust
? cd thread_demo

thread_demo on  master [?] via ?? 1.67.1
? c # code .

thread_demo on  master [?] via ?? 1.67.1
?

  • thread::sleep 會導致當前線程暫停執(zhí)行
use std::thread;
use std::time::Duration;

fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));  // 暫停 1 毫秒
    }
}

執(zhí)行

thread_demo on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cargo run            
   Compiling thread_demo v0.1.0 (/Users/qiaopengjun/rust/thread_demo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.65s
     Running `target/debug/thread_demo`
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 4 from the main thread!
hi number 5 from the spawned thread!

thread_demo on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? 

通過 join Handle 來等待所有線程的完成

  • thread::spawn 函數(shù)的返回值類型是 JoinHandle
  • JoinHandle 持有值的所有權(quán)
    • 調(diào)用其 join 方法,可以等待對應的其它線程的完成
  • join 方法:調(diào)用 handle 的join方法會阻止當前運行線程的執(zhí)行,直到 handle 所表示的這些線程終結(jié)。
use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));  // 暫停 1 毫秒
    }

    handle.join().unwrap();
}

執(zhí)行

thread_demo on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cargo run
   Compiling thread_demo v0.1.0 (/Users/qiaopengjun/rust/thread_demo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.75s
     Running `target/debug/thread_demo`
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 2 from the main thread!
hi number 3 from the spawned thread!
hi number 3 from the main thread!
hi number 4 from the spawned thread!
hi number 4 from the main thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!

thread_demo on  master [?] is ?? 0.1.0 via ?? 1.67.1 

等分線程執(zhí)行完繼續(xù)執(zhí)行主線程

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    handle.join().unwrap();

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1)); // 暫停 1 毫秒
    }
}

運行

thread_demo on  master [?] is ?? 0.1.0 via ?? 1.67.1 
? cargo run
   Compiling thread_demo v0.1.0 (/Users/qiaopengjun/rust/thread_demo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.28s
     Running `target/debug/thread_demo`
hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!
hi number 1 from the main thread!
hi number 2 from the main thread!
hi number 3 from the main thread!
hi number 4 from the main thread!

thread_demo on  master [?] is ?? 0.1.0 via ?? 1.67.1 

使用 move 閉包

  • move 閉包通常和 thread::spawn 函數(shù)一起使用,它允許你使用其它線程的數(shù)據(jù)
  • 創(chuàng)建線程時,把值的所有權(quán)從一個線程轉(zhuǎn)移到另一個線程
use std::thread;

fn main() {
  let v = vec![1, 2, 3];
  let handle = thread::spawn(|| { // 報錯
    println!("Here's a vector: {:?}", v);
  });
  
  // drop(v);
  handle.join().unwrap();
}

修改后:文章來源地址http://www.zghlxwxcb.cn/news/detail-418201.html

use std::thread;

fn main() {
  let v = vec![1, 2, 3];
  let handle = thread::spawn(move || { 
    println!("Here's a vector: {:?}", v);
  });
  
  // drop(v);
  handle.join().unwrap();
}

二、使用消息傳遞來跨線程傳遞數(shù)據(jù)

消息傳遞

  • 一種很流行且能保證安全并發(fā)的技術(shù)就是:消息傳遞。
    • 線程(或 Actor)通過彼此發(fā)送消息(數(shù)據(jù))來進行通信
  • Go 語言的名言:不要用共享內(nèi)存來通信,要用通信來共享內(nèi)存。
  • Rust:Channel(標準庫提供)

Channel

  • Channel 包含: 發(fā)送端、接收端
  • 調(diào)用發(fā)送端的方法,發(fā)送數(shù)據(jù)
  • 接收端會檢查和接收到達的數(shù)據(jù)
  • 如果發(fā)送端、接收端中任意一端被丟棄了,那么Channel 就”關(guān)閉“了

創(chuàng)建 Channel

  • 使用 mpsc::channel函數(shù)來創(chuàng)建 Channel
    • mpsc 表示 multiple producer,single consumer(多個生產(chǎn)者、一個消費者)
    • 返回一個 tuple(元組):里面元素分別是發(fā)送端、接收端
use std::sync::mpsc;
use std::thread;

fn main() {
  let (tx, rx) = mpsc::channel();
  
  thread::spawn(move || {
    let val = String::from("hi");
    tx.send(val).unwrap();
  });
  
  let received = rx.recv().unwrap();
  println!("Got: {}", received);
}

發(fā)送端的 send 方法

  • 參數(shù):想要發(fā)送的數(shù)據(jù)
  • 返回:Result<T, E>
    • 如果有問題(例如接收端已經(jīng)被丟棄),就返回一個錯誤

接收端的方法

  • recv 方法:阻止當前線程執(zhí)行,直到 Channel 中有值被送來
    • 一旦有值收到,就返回 Result<T, E>
    • 當發(fā)送端關(guān)閉,就會收到一個錯誤
  • try_recv 方法:不會阻塞,
    • 立即返回 Result<T, E>:
      • 有數(shù)據(jù)達到:返回 Ok,里面包含著數(shù)據(jù)
      • 否則,返回錯誤
    • 通常會使用循環(huán)調(diào)用來檢查 try_recv 的結(jié)果

Channel 和所有權(quán)轉(zhuǎn)移

  • 所有權(quán)在消息傳遞中非常重要:能幫你編寫安全、并發(fā)的代碼
use std::sync::mpsc;
use std::thread;

fn main() {
  let (tx, rx) = mpsc::channel();
  
  thread::spawn(move || {
    let val = String::from("hi");
    tx.send(val).unwrap();
    println!("val is {}", val)  // 報錯 借用了移動的值
  });
  
  let received = rx.recv().unwrap();
  println!("Got: {}", received);
}

發(fā)送多個值,看到接收者在等待

use std::sync::mpsc;
use std::thread;

fn main() {
  let (tx, rx) = mpsc::channel();
  
  thread::spawn(move || {
    let vals = vec![
      String::from("hi"),
      String::from("from"),
      String::from("the"),
      String::from("thread"),
    ];
    
    for val in vals {
      tx.send(val).unwrap();
      thread::sleep(Duration::from_millis(1));
    }  
  });
  
  for received in rx {
    println!("Got: {}", received);
  }
}

通過克隆創(chuàng)建多個發(fā)送者

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
  let (tx, rx) = mpsc::channel();
  
  let tx1 = mpsc::Sender::clone(&tx);
  thread::spawn(move || {
    let vals = vec![
      String::from("1: hi"),
      String::from("1: from"),
      String::from("1: the"),
      String::from("1: thread"),
    ];
    
    for val in vals {
      tx1.send(val).unwrap();
      thread::sleep(Duration::from_millis(1));
    }  
  });
   thread::spawn(move || {
    let vals = vec![
      String::from("hi"),
      String::from("from"),
      String::from("the"),
      String::from("thread"),
    ];
    
    for val in vals {
      tx.send(val).unwrap();
      thread::sleep(Duration::from_millis(1));
    }  
  });
  
  for received in rx {
    println!("Got: {}", received);
  }
}

三、共享狀態(tài)的并發(fā)

使用共享來實現(xiàn)并發(fā)

  • Go 語言的名言:不要用共享內(nèi)存來通信,要用通信來共享內(nèi)存。
  • Rust支持通過共享狀態(tài)來實現(xiàn)并發(fā)。
  • Channel 類似單所有權(quán):一旦將值的所有權(quán)轉(zhuǎn)移至 Channel,就無法使用它了
  • 共享內(nèi)存并發(fā)類似多所有權(quán):多個線程可以同時訪問同一塊內(nèi)存

使用 Mutex 來每次只允許一個線程來訪問數(shù)據(jù)

  • Mutex 是 mutual exclusion(互斥鎖)的簡寫
  • 在同一時刻,Mutex 只允許一個線程來訪問某些數(shù)據(jù)
  • 想要訪問數(shù)據(jù):
    • 線程必須首先獲取互斥鎖(lock)
      • lock 數(shù)據(jù)結(jié)構(gòu)是 mutex 的一部分,它能跟蹤誰對數(shù)據(jù)擁有獨占訪問權(quán)
    • mutex 通常被描述為:通過鎖定系統(tǒng)來保護它所持有的數(shù)據(jù)

Mutex 的兩條規(guī)則

  • 在使用數(shù)據(jù)之前,必須嘗試獲取鎖(lock)。
  • 使用完 mutex 所保護的數(shù)據(jù),必須對數(shù)據(jù)進行解鎖,以便其它線程可以獲取鎖。

Mutex<T> 的 API

  • 通過 Mutex::new(數(shù)據(jù)) 來創(chuàng)建 Mutex<T>
    • Mutex<T>是一個智能指針
  • 訪問數(shù)據(jù)前,通過 lock 方法來獲取鎖
    • 會阻塞當前線程
    • lock 可能會失敗
    • 返回的是 MutexGuard(智能指針,實現(xiàn)了 Deref 和 Drop)
use std::sync::Mutex;

fn main() {
  let m = Mutex::new(5);
  
  {
    let mut num = m.lock().unwrap();
    *num = 6;
  }
  
  println!("m = {:?}", m);
}

多線程共享 Mutex<T>

use std::sync::Mutex;
use std::thread;

fn main() {
  let counter = Mutex::new(0);
  let mut handles = vec![];
  
  for _ in 0..10 {
     let handle = thread::spawn(move || {  // 報錯 循環(huán) 所有權(quán)
       let mut num = counter.lock().unwrap();
       
       *num += 1;
    });
    handles.push(handle);
  }
  
  for handle in handles {
    handle.join().unwrap();
  }
  
  println!("Result: {}", *counter.lock().unwrap());
}

多線程的多重所有權(quán)

use std::sync::Mutex;
use std::thread;
use std::rc::Rc;

fn main() {
  let counter = Rc::new(Mutex::new(0));
  let mut handles = vec![];
  
  for _ in 0..10 {
    let counter = Rc::clone(&counter);
    let handle = thread::spawn(move || {  // 報錯 rc 只能用于單線程
       let mut num = counter.lock().unwrap();
       
       *num += 1;
    });
    handles.push(handle);
  }
  
  for handle in handles {
    handle.join().unwrap();
  }
  
  println!("Result: {}", *counter.lock().unwrap());
}

使用 Arc<T>來進行原子引用計數(shù)

  • Arc<T>Rc<T>類似,它可以用于并發(fā)情景
    • A:atomic,原子的
  • 為什么所有的基礎(chǔ)類型都不是原子的,為什么標準庫類型不默認使用 Arc<T>?
    • 需要性能作為代價
  • Arc<T>Rc<T> 的API是相同的
use std::sync::{Mutex, Arc};
use std::thread;

fn main() {
  let counter = Arc::new(Mutex::new(0));
  let mut handles = vec![];
  
  for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {  
       let mut num = counter.lock().unwrap();
       
       *num += 1;
    });
    handles.push(handle);
  }
  
  for handle in handles {
    handle.join().unwrap();
  }
  
  println!("Result: {}", *counter.lock().unwrap());
}

RefCell<T>/Rc<T> vs Muter<T>/Arc<T>

  • Mutex<T>提供了內(nèi)部可變性,和 Cell 家族一樣
  • 我們使用 RefCell<T>來改變 Rc<T>里面的內(nèi)容
  • 我們使用 Mutex<T> 來改變 Arc<T> 里面的內(nèi)容
  • 注意:Mutex<T> 有死鎖風險

四、通過 Send 和 Sync Trait 來擴展并發(fā)

Send 和 Sync trait

  • Rust 語言的并發(fā)特性較少,目前講的并發(fā)特性都來自標準庫(而不是語言本身)
  • 無需局限于標準庫的并發(fā),可以自己實現(xiàn)并發(fā)
  • 但在Rust語言中有兩個并發(fā)概念:
    • std::marker::Sync 和 std::marker::Send 這兩個trait

Send:允許線程間轉(zhuǎn)移所有權(quán)

  • 實現(xiàn) Send trait 的類型可在線程間轉(zhuǎn)移所有權(quán)
  • Rust中幾乎所有的類型都實現(xiàn)了 Send
    • Rc<T> 沒有實現(xiàn) Send,它只用于單線程情景
  • 任何完全由Send 類型組成的類型也被標記為 Send
  • 除了原始指針之外,幾乎所有的基礎(chǔ)類型都是 Send

Sync:允許從多線程訪問

  • 實現(xiàn)Sync的類型可以安全的被多個線程引用
  • 也就是說:如果T是Sync,那么 &T 就是 Send
    • 引用可以被安全的送往另一個線程
  • 基礎(chǔ)類型都是 Sync
  • 完全由 Sync 類型組成的類型也是 Sync
    • 但,Rc<T>不是 Sync 的
    • RefCell<T>Cell<T>家族也不是 Sync的
    • 而,Mutex<T>是Sync的

手動來實現(xiàn) Send 和 Sync 是不安全的

到了這里,關(guān)于Rust編程語言入門之無畏并發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • Rust編程語言入門之模式匹配

    模式是Rust中的一種特殊語法,用于匹配復雜和簡單類型的結(jié)構(gòu) 將模式與匹配表達式和其他構(gòu)造結(jié)合使用,可以更好地控制程序的控制流 模式由以下元素(的一些組合)組成: 字面值 解構(gòu)的數(shù)組、enum、struct 和 tuple 變量 通配符 占位符 想要使用模式,需要將其與某個值進行

    2023年04月22日
    瀏覽(23)
  • Rust編程語言入門之智能指針

    指針:一個變量在內(nèi)存中包含的是一個地址(指向其它數(shù)據(jù)) Rust 中最常見的指針就是”引用“ 引用: 使用 借用它指向的值 沒有其余開銷 最常見的指針類型 智能指針是這樣一些數(shù)據(jù)結(jié)構(gòu): 行為和指針相似 有額外的元數(shù)據(jù)和功能 通過記錄所有者的數(shù)量,使一份數(shù)據(jù)被多個

    2023年04月16日
    瀏覽(26)
  • Rust編程語言入門之函數(shù)式語言特性:-迭代器和閉包

    閉包(closures) 迭代器(iterators) 優(yōu)化改善 12 章的實例項目 討論閉包和迭代器的運行時性能 閉包:可以捕獲其所在環(huán)境的匿名函數(shù)。 閉包: 是匿名函數(shù) 保存為變量、作為參數(shù) 可在一個地方創(chuàng)建閉包,然后在另一個上下文中調(diào)用閉包來完成運算 可從其定義的作用域捕獲值

    2023年04月08日
    瀏覽(27)
  • Rust編程語言入門之cargo、crates.io

    通過 release profile 來自定義構(gòu)建 在https://crates.io/上發(fā)布庫 通過 workspaces 組織大工程 從 https://crates.io/來安裝庫 使用自定義命令擴展 cargo release profile: 是預定義的 可自定義:可使用不同的配置,對代碼編譯擁有更多的控制 每個 profile 的配置都獨立于其它的 profile cargo 主要的

    2023年04月09日
    瀏覽(28)
  • Rust編程語言入門之最后的項目:多線程 Web 服務器

    在 socket 上監(jiān)聽 TCP 連接 解析少量的 HTTP 請求 創(chuàng)建一個合適的 HTTP 響應 使用線程池改進服務器的吞吐量 優(yōu)雅的停機和清理 注意:并不是最佳實踐 創(chuàng)建項目 main.rs 文件 修改一: 修改二: 修改三: 修改四: 修改五: hello.html 文件 404.html 文件 單線程Web服務器 開啟線程 lib.r

    2023年04月25日
    瀏覽(24)
  • Rust 是一種面向系統(tǒng)編程語言 主要被設(shè)計用來解決執(zhí)行速度、安全性、并發(fā)性和可靠性等方面的問題 Rust From First Principles: Building a Minimal Rust

    作者:禪與計算機程序設(shè)計藝術(shù) Rust 是一種面向系統(tǒng)編程語言,主要被設(shè)計用來解決執(zhí)行速度、安全性、并發(fā)性和可靠性等方面的問題。相比于其他語言來說,它擁有以下優(yōu)點: 高性能: Rust 的運行時是單線程的,但是擁有基于垃圾收集(GC)的自動內(nèi)存管理機制,使得在開

    2024年02月07日
    瀏覽(37)
  • 【Rust】Rust學習 第十六章無畏并發(fā)

    【Rust】Rust學習 第十六章無畏并發(fā)

    安全且高效的處理并發(fā)編程是 Rust 的另一個主要目標。 并發(fā)編程( Concurrent programming ),代表程序的不同部分相互獨立的執(zhí)行,而?并行編程( parallel programming )代表程序不同部分于同時執(zhí)行 ,這兩個概念隨著計算機越來越多的利用多處理器的優(yōu)勢時顯得愈發(fā)重要。由于歷

    2024年02月12日
    瀏覽(15)
  • GO語言網(wǎng)絡編程(并發(fā)編程)并發(fā)介紹,Goroutine

    GO語言網(wǎng)絡編程(并發(fā)編程)并發(fā)介紹,Goroutine

    進程和線程 并發(fā)和并行 協(xié)程和線程 協(xié)程:獨立的??臻g,共享堆空間,調(diào)度由用戶自己控制,本質(zhì)上有點類似于用戶級線程,這些用戶級線程的調(diào)度也是自己實現(xiàn)的。 線程:一個線程上可以跑多個協(xié)程,協(xié)程是輕量級的線程。 goroutine 只是由官方實現(xiàn)的超級\\\"線程池\\\"。 每個

    2024年02月09日
    瀏覽(91)
  • GO語言網(wǎng)絡編程(并發(fā)編程)Channel

    GO語言網(wǎng)絡編程(并發(fā)編程)Channel

    1.1.1 Channel 單純地將函數(shù)并發(fā)執(zhí)行是沒有意義的。函數(shù)與函數(shù)間需要交換數(shù)據(jù)才能體現(xiàn)并發(fā)執(zhí)行函數(shù)的意義。 雖然可以使用共享內(nèi)存進行數(shù)據(jù)交換,但是共享內(nèi)存在不同的goroutine中容易發(fā)生競態(tài)問題。為了保證數(shù)據(jù)交換的正確性,必須使用互斥量對內(nèi)存進行加鎖,這種做法勢

    2024年02月09日
    瀏覽(104)
  • GO語言網(wǎng)絡編程(并發(fā)編程)select

    1.1.1 select多路復用 在某些場景下我們需要同時從多個通道接收數(shù)據(jù)。通道在接收數(shù)據(jù)時,如果沒有數(shù)據(jù)可以接收將會發(fā)生阻塞。你也許會寫出如下代碼使用遍歷的方式來實現(xiàn): 這種方式雖然可以實現(xiàn)從多個通道接收值的需求,但是運行性能會差很多。為了應對這種場景,G

    2024年02月09日
    瀏覽(239)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包