Concurrency
Compréhension
concurrence/, message(Message-passing) partagé(Shared-state)
Thread
concurrenceRemarque
Race conditions, where threads are accessing data or resources in an inconsistent order
Deadlocks, where two threads are waiting for each other to finish using a resource the other thread has, preventing both threads from continuing
BUG
Bugs that happen only in certain situations and are hard to reproduce and fix reliably
Green Thread thread, thread
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| { // Remarque这里的闭包如果需要入参,需要显式声明 move
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1)); // 此时操作系统有机会切换执行其他thread
}
});
handle.join().unwrap(); // 使用 join 等待所有thread执行结束(否则后续代码不执行)
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}
Message-passing
Channel
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); }Multi producer/transmitter && Receive multi message
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)); } }); // Remarque较于上例,使用了 for-in loop for received in rx { println!("Got: {}", received); } }
Shared-state
Mutex(Mutual Exclusion,),:,thread
thread lock mutex 。lock mutex , mutex 。,。
,Mutexes :
,
You must attempt to acquire the lock before using the data.
mutex ,,thread
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 type Erreur、
Mutex
use std::sync::Mutex; fn main() { let m = Mutex::new(5); { let mut num = m.lock().unwrap(); // appel lock 获取锁 *num = 6; } println!("m = {:?}", m); }Mutex<T>;,lockretourLockResult,unwrapretourMutexGuard;MutexGuardDereftrait ,DroptraitMutexGuard。,thread,。threadpartagé 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 type, ownership thread
Sync trait type,thread
,Sync Send
Origin
…
Atomic Reference Counting with Arc<T>
Fortunately, Arc<T> is a type like Rc<T> that is safe to use in concurrent situations. The a stands for atomic, meaning it’s an atomically reference counted type. Atomics are an additional kind of concurrency primitive that we won’t cover in detail here: see the standard library documentation for [std::sync::atomic](https://doc.rust-lang.org/std/sync/atomic/index.html) for more details. At this point, you just need to know that atomics work like primitive types but are safe to share across threads.
You might then wonder why all primitive types aren’t atomic and why standard library types aren’t implemented to use Arc<T> by default. The reason is that thread safety comes with a performance penalty that you only want to pay when you really need to. If you’re just performing operations on values within a single thread, your code can run faster if it doesn’t have to enforce the guarantees atomics provide.
Let’s return to our example: Arc<T> and Rc<T> have the same API, so we fix our program by changing the use line, the call to new, and the call to clone. The code in Listing 16-15 will finally compile and run:
…