一、簡介
多線程 是一種并發(fā)執(zhí)行的技術(shù),它允許一個程序或進程同時執(zhí)行多個線程。每個線程都是程序執(zhí)行的一個獨立路徑,它們可以并行運行,共享進程的資源(如內(nèi)存空間),但每個線程有自己的指令指針、堆棧和局部變量。多線程的主要目的是提高程序的執(zhí)行效率,通過同時執(zhí)行多個任務(wù)來充分利用計算機的多核處理器。
- 在Rust語言中,多線程編程是通過標(biāo)準(zhǔn)庫中的
std::thread
模塊來實現(xiàn)的。Rust提供了創(chuàng)建和管理線程的API,以及用于線程間同步和通信的機制,如互斥鎖(Mutex)和通道(Channel)。
二、創(chuàng)建線程
1.創(chuàng)建一個線程
use std::thread;
fn main() {
// 創(chuàng)建一個新線程
let handle = thread::spawn(|| {
// 在新線程中執(zhí)行的代碼
println!("Hello from a new thread!");
});
// 等待線程結(jié)束
handle.join().unwrap();
}
2.創(chuàng)建多個線程
use std::thread;
fn main() {
// 創(chuàng)建一個向量來存儲線程的句柄
let mut threads = vec![];
// 創(chuàng)建多個線程
for i in 0..5 {
// 使用閉包捕獲變量i的值
let thread_number = i;
let handle = thread::spawn(move || {
// 在新線程中打印線程編號
println!("線程 {} 正在運行", thread_number);
});
// 將線程句柄添加到向量中
threads.push(handle);
}
// 等待所有線程完成
for handle in threads {
handle.join().unwrap();
}
// 在主線程中打印一些信息
for i in 0..5 {
println!("主線程打印數(shù)字: {}", i);
}
}
# 輸出結(jié)果:
線程 0 正在運行
線程 1 正在運行
線程 2 正在運行
線程 3 正在運行
線程 4 正在運行
主線程打印數(shù)字: 0
主線程打印數(shù)字: 1
主線程打印數(shù)字: 2
主線程打印數(shù)字: 3
主線程打印數(shù)字: 4
- 從輸出結(jié)果上看,仍然像是順序執(zhí)行,所以這里引入一個休眠,讓線程執(zhí)行的時候隨機休眠0-3秒。
生成隨機數(shù)
由于Rust核心語言中沒有隨機數(shù)生成的函數(shù),需要使用rand庫來進行
# 首先需要在Cargo.toml中添加以下內(nèi)容
[dependencies]
rand = "0.8"
# 然后在代碼中用use 引入
use rand::Rng;
use rand::thread_rng;
fn main() {
let mut rng = thread_rng();
for _i in 0..10{
let random_number = rng.gen_range(1..4);
println!("隨機數(shù)是: {}", random_number);
}
}
# 結(jié)果:
隨機數(shù)是: 3
隨機數(shù)是: 1
隨機數(shù)是: 3
隨機數(shù)是: 1
隨機數(shù)是: 3
隨機數(shù)是: 1
隨機數(shù)是: 3
隨機數(shù)是: 2
隨機數(shù)是: 3
隨機數(shù)是: 1
隨機數(shù)生成的區(qū)間與循環(huán)一樣,是一個前閉后開的區(qū)間
嘗試讓程序睡一會兒
use rand::Rng;
use std::{thread::sleep, time::Duration};
fn main() {
// 創(chuàng)建一個隨機數(shù)生成器
let mut rng = rand::thread_rng();
// 生成一個0到3之間的隨機秒數(shù)
let random_seconds: u64 = rng.gen_range(0..4);
// 將秒數(shù)轉(zhuǎn)換為Duration
let duration = Duration::from_secs(random_seconds);
// 讓當(dāng)前線程睡眠指定的時間
sleep(duration);
// 之后的代碼會在等待后執(zhí)行
println!("等待了 {} 秒", random_seconds);
}
引入多線程
use rand::Rng;
use std::{thread::sleep, time::Duration};
use std::thread;
fn main() {
// 創(chuàng)建一個向量來存儲線程的句柄
let mut threads = vec![];
// 創(chuàng)建多個線程
for i in 1..=10 {
// 使用閉包捕獲變量i的值
let thread_number = i;
let handle = thread::spawn(move || {
// 在新線程中打印線程編號
println!("線程 {} 正在運行", thread_number);
// 創(chuàng)建一個隨機數(shù)生成器
let mut rng = rand::thread_rng();
// 生成一個0到3之間的隨機秒數(shù)
let random_seconds: u64 = rng.gen_range(0..4);
// 將秒數(shù)轉(zhuǎn)換為Duration
let duration = Duration::from_secs(random_seconds);
// 讓當(dāng)前線程睡眠指定的時間
sleep(duration);
println!("線程 {} 運行結(jié)束,休息了{}秒.", thread_number,random_seconds);
});
// 將線程句柄添加到向量中
threads.push(handle);
}
// 等待所有線程完成
for handle in threads {
handle.join().unwrap();
}
// 在主線程中打印一些信息
for i in 0..5 {
println!("主線程打印數(shù)字: {}", i);
}
}
# 輸出結(jié)果
線程 3 正在運行
線程 2 正在運行
線程 5 正在運行
線程 7 正在運行
線程 7 運行結(jié)束,休息了0秒.
線程 4 正在運行
線程 6 正在運行
線程 1 正在運行
線程 8 正在運行
線程 9 正在運行
線程 10 正在運行
線程 6 運行結(jié)束,休息了1秒.
線程 4 運行結(jié)束,休息了1秒.
線程 3 運行結(jié)束,休息了1秒.
線程 9 運行結(jié)束,休息了1秒.
線程 1 運行結(jié)束,休息了2秒.
線程 10 運行結(jié)束,休息了2秒.
線程 5 運行結(jié)束,休息了2秒.
線程 2 運行結(jié)束,休息了3秒.
線程 8 運行結(jié)束,休息了3秒.
主線程打印數(shù)字: 0
主線程打印數(shù)字: 1
主線程打印數(shù)字: 2
主線程打印數(shù)字: 3
主線程打印數(shù)字: 4
三、線程返回值的處理
對于有返回值的多線程來說有兩種情況,一種是每個線程處理一個獨立的值,用向量接收,另一種是多個線程處理一個值。
1.每個線程處理一個獨立的值
use std::thread;
fn main() {
let mut handles = vec![];
for i in 0..5 {
let handle = thread::spawn(move || {
return i*i;
});
handles.push(handle);
}
let mut results = vec![];
for handle in handles {
match handle.join() {
Ok(value) => results.push(value),
Err(e) => println!("Thread panicked: {:?}", e),
}
}
println!("Results: {:?}", results); //Results: [0, 1, 4, 9, 16]
}
2.多個線程處理一個值
由于多個線程處理一個值,可能造成條件競爭,屬于線程不安全行為,Rust語言中提供了3種處理行為。
- Arc 只讀訪問,用于共享只讀數(shù)據(jù),通過原子引用計數(shù)管理生命周期。
- Mutex 互斥鎖,用于保護數(shù)據(jù),確保一次只有一個線程可以訪問數(shù)據(jù)(提供獨占訪問)。
- RwLock 讀寫鎖,用于保護數(shù)據(jù),但允許多個讀者同時訪問,寫者必須獨占訪問。
Arc(原子引用計數(shù))
Arc
是一個提供共享所有權(quán)的智能指針。它用于在多個所有者之間共享數(shù)據(jù),且只允許對這些數(shù)據(jù)進行只讀訪問。Arc通過原子操作維護一個引用計數(shù),確保數(shù)據(jù)的生命周期至少與最長的所有者一樣長。當(dāng)最后一個Arc指針被丟棄時,其指向的數(shù)據(jù)也會被釋放。
use std::sync::Arc;
use std::thread;
fn main() {
// 創(chuàng)建一個要在多個線程之間共享的值
let data = Arc::new(vec![1, 2, 3, 4, 5]);
// 創(chuàng)建一個向量來存儲線程的句柄
let mut handles = vec![];
println!("Thread {:?} is reading value: {:?}", thread::current().id(), &data); // 主線程的線程ID為 1
// 創(chuàng)建幾個線程來只讀訪問數(shù)據(jù)
for _i in 0..data.len() {
let data = data.clone(); // 克隆Arc以便在線程中使用
let handle = thread::spawn(move || {
// 獲取Vec的引用以便索引
// 使用 {:?} 來打印 ThreadId
println!("Thread {:?} is reading value: {:?}", thread::current().id(), &data);
});
handles.push(handle);
}
// 等待所有線程完成
for handle in handles {
handle.join().unwrap();
}
}
# 運行結(jié)果
Thread ThreadId(1) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(2) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(3) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(4) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(5) is reading value: [1, 2, 3, 4, 5]
Thread ThreadId(6) is reading value: [1, 2, 3, 4, 5]
Mutex(互斥鎖)
Mutex
是一個提供互斥訪問的智能指針。它用于保護數(shù)據(jù),確保一次只有一個線程能夠訪問數(shù)據(jù)。當(dāng)一個線程擁有Mutex的鎖時,其他嘗試獲取鎖的線程將被阻塞,直到鎖被釋放。
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// 創(chuàng)建一個Arc包裹的互斥鎖和值
let counter = Arc::new(Mutex::new(1));
let mut handles = vec![];
// 創(chuàng)建幾個線程來增加計數(shù)器
for i in 1..10 {
// 克隆Arc智能指針,而不是Mutex或它的值
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
// 獲取互斥鎖以便修改值
let mut num = counter.lock().unwrap();
*num *=i;
});
handles.push(handle);
}
// 等待所有線程完成
for handle in handles {
handle.join().unwrap();
}
// 輸出最終計數(shù)器的值
println!("Result: {}", *counter.lock().unwrap()); // Result: 362880
}
RwLock(讀寫鎖)
RwLock
是一個提供讀寫鎖定的智能指針。與Mutex不同,RwLock允許多個讀者同時訪問數(shù)據(jù),但寫者必須獨占鎖。當(dāng)寫者擁有鎖時,任何嘗試獲取讀鎖或?qū)戞i的線程都將被阻塞。當(dāng)沒有寫者時,可以有多個讀者同時訪問數(shù)據(jù)。文章來源:http://www.zghlxwxcb.cn/news/detail-835161.html
use std::sync::{Arc, RwLock};
fn main() {
let data = Arc::new(RwLock::new(0));
let mut handles = vec![];
// 創(chuàng)建多個讀線程
for i in 0..5 {
let data = Arc::clone(&data);
let handle = std::thread::spawn(move || {
let num = data.read().unwrap();
println!("Thread {} Reading value: {}", i,*num);
});
handles.push(handle);
}
// 創(chuàng)建一個寫線程
let data = Arc::clone(&data);
let handle = std::thread::spawn(move || {
let mut num = data.write().unwrap();
*num += 1;
println!("Writing value: {}", *num);
});
handles.push(handle);
for handle in handles {
handle.join().unwrap();
}
}
# 第一次執(zhí)行結(jié)果
Thread 0 Reading value: 0
Thread 3 Reading value: 0
Thread 1 Reading value: 0
Thread 4 Reading value: 0
Thread 2 Reading value: 0
Writing value: 1
# 第二次執(zhí)行結(jié)果
Thread 0 Reading value: 0
Thread 3 Reading value: 0
Thread 1 Reading value: 0
Thread 4 Reading value: 0
Thread 2 Reading value: 0
Writing value: 1
這里有一個問題,就是如果寫線程最后執(zhí)行,那么讀線程讀的都是原始數(shù)據(jù),如果寫線程先執(zhí)行,那么讀的就是修改后的數(shù)據(jù),所以對讀寫順序有要求的話應(yīng)該做好時序的控制文章來源地址http://www.zghlxwxcb.cn/news/detail-835161.html
到了這里,關(guān)于Rust語言之多線程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!