Rust - Grundlagen - 15 - Nebenläufigkeit

Nebenläufigkeit

Verständnis

Nebenläufigkeit und Parallelität lassen sich mit Nachrichtenübermittlung (Message-passing) oder gemeinsamem Zustand (Shared-state) umsetzen.

Threads

Bei Nebenläufigkeit sind folgende Punkte wichtig:

  • Datenrennen

    Race conditions, where threads are accessing data or resources in an inconsistent order

  • Deadlocks

    Deadlocks, where two threads are waiting for each other to finish using a resource the other thread has, preventing both threads from continuing

  • Schwer reproduzierbare Bugs

    Bugs that happen only in certain situations and are hard to reproduce and fix reliably

Green Threads sind Threads auf Sprachebene und nicht identisch mit den Threads des Betriebssystems.

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

fn main() {
    let handle = thread::spawn(|| {  // 注意这里的闭包如果需要入参,需要显式声明 move
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));  // 此时操作系统有机会切换执行其他线程
        }
    });

    handle.join().unwrap();  // 使用 join 等待所有线程执行结束(否则后续代码不执行)

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

Nachrichtenübermittlung

  • Grundlegende Channel-Syntax

    use std::sync::mpsc;  // multiple producer single consumer
    
    fn main() {
        let (tx, rx) = mpsc::channel();  // transmitter, receiver
    
    		thread::spawn(move || {
            let val = String::from("hi");
            tx.send(val).unwrap();  // 此时 val moved ownership
        });
    
    		let received = rx.recv().unwrap();  // 如果使用 try_recv 则不会阻塞
        println!("Got: {}", received);
    }
    
  • Mehrere Producer/Transmitter und mehrere Nachrichten empfangen

    use std::sync::mpsc;
    use std::thread;
    use std::time::Duration;
    
    fn main() {
        let (tx, rx) = mpsc::channel();
    
    		// 因为后续闭包内会 move tx 的 ownership
        // 所以这里需要提前 clone 出 tx1 实现 multi producer
    		let tx1 = tx.clone();
    		thread::spawn(move || {
            let vals = vec![
                String::from("hi"),
                String::from("from"),
                String::from("the"),
                String::from("thread"),
            ];
    
            for val in vals {
                tx1.send(val).unwrap();
                thread::sleep(Duration::from_secs(1));
            }
        });
    
        thread::spawn(move || {
            let vals = vec![
                String::from("more"),
                String::from("messages"),
                String::from("for"),
                String::from("you"),
            ];
    
            for val in vals {
                tx.send(val).unwrap();
                thread::sleep(Duration::from_secs(1));
            }
        });
    
    		// 注意较于上例,使用了 for-in loop
        for received in rx {  
            println!("Got: {}", received);
        }
    }
    

Shared-state

Mutex(Mutual Exclusion,),:,

lock mutex 。lock mutex , mutex 。,。

,Mutexes :

  • ,

    You must attempt to acquire the lock before using the data.

  • mutex ,,

    When you’re done with the data that the mutex guards, you must unlock the data so other threads can acquire the lock.

mutexex , channels 。,Rust 、

  • Mutex

    use std::sync::Mutex;
    
    fn main() {
        let m = Mutex::new(5);
    
        {
            let mut num = m.lock().unwrap();  // 调用 lock 获取锁
            *num = 6;
        }
    
        println!("m = {:?}", m);
    }
    

    Mutex<T> ;,lock LockResult, unwrap MutexGuard;MutexGuard Deref trait , Drop trait MutexGuard 。,,。

  • Mutex

    Atomic Reference Counted Type, Rc<T>

    use std::sync::{Arc, Mutex};
    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());
    }
    

Send & Sync trait

Send trait , ownership

Sync trait ,

,Sync Send

Quelle

https://doc.rust-lang.org/book/ch16-00-concurrency.html

…

Rust bietet sichere Abstraktionen für Threads, Nachrichtenkanäle und gemeinsam genutzten Zustand. Der Compiler hilft, Datenrennen und ungültige Speicherzugriffe zu verhindern. Mit Mutex<T> und Arc<T> können mehrere Threads Zustand sicher teilen; Kanäle übertragen Daten zwischen Threads.