并发编程:Rust 中的线程、消息传递与共享状态
Rust 提供了强大的并发编程支持,包括线程、消息传递和共享状态。通过线程,我们可以并行执行代码;通过消息传递,线程可以安全地通信;通过共享状态,线程可以安全地共享数据。本文将详细介绍 Rust 中的并发编程,并通过完整的代码示例和详尽的指导过程帮助读者深入理解这些概念。
1. 线程
1.1 创建线程
Rust 使用 std::thread 模块来创建和管理线程。通过 spawn 函数,我们可以创建一个新线程。
示例 1:创建线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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));
}
handle.join().unwrap(); // 等待线程结束
}
|
解释:
thread::spawn(|| { ... }) 创建一个新线程,并执行闭包中的代码。
handle.join().unwrap() 等待线程结束,确保主线程不会提前退出。
1.2 线程间传递数据
线程可以通过闭包捕获外部变量来传递数据。
示例 2:线程间传递数据
1
2
3
4
5
6
7
8
9
10
11
|
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
}
|
解释:
move 关键字将 v 的所有权转移到线程中。
- 线程可以访问
v,但主线程不能再使用 v。
2. 消息传递
2.1 使用通道进行线程间通信
Rust 使用 std::sync::mpsc 模块提供的通道(channel)进行线程间通信。通道由发送端(sender)和接收端(receiver)组成。
示例 3:使用通道发送消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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);
}
|
解释:
mpsc::channel() 创建一个通道,返回发送端 tx 和接收端 rx。
tx.send(val).unwrap() 发送消息到通道。
rx.recv().unwrap() 从通道接收消息。
2.2 多生产者单消费者
Rust 的通道支持多生产者单消费者(MPSC)模式。
示例 4:多生产者单消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
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(std::time::Duration::from_millis(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(std::time::Duration::from_millis(1));
}
});
for received in rx {
println!("Got: {}", received);
}
}
|
解释:
tx.clone() 克隆发送端,允许多个线程发送消息。
rx 是接收端,可以接收所有发送端的消息。
3. 共享状态
3.1 使用 Mutex 实现共享内存
Mutex(互斥锁)用于保护共享数据,确保同一时间只有一个线程可以访问数据。
示例 5:使用 Mutex 保护共享数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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());
}
|
解释:
Arc(原子引用计数)允许多个线程共享所有权。
Mutex 保护共享数据,确保线程安全。
counter.lock().unwrap() 获取锁,并返回一个可变引用。
3.2 使用 Arc 实现多线程共享所有权
Arc 是 Rc 的线程安全版本,允许多个线程共享所有权。
示例 6:使用 Arc 共享所有权
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let mut data = data.lock().unwrap();
*data += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *data.lock().unwrap());
}
|
解释:
Arc::clone(&data) 增加引用计数,允许多个线程共享所有权。
data.lock().unwrap() 获取锁,并返回一个可变引用。
4. 综合示例
以下是一个综合示例,展示了线程、消息传递和共享状态的结合使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for i in 0..10 {
let tx = tx.clone();
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
tx.send(*num).unwrap();
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
for received in rx.try_iter() {
println!("Received: {}", received);
}
println!("Result: {}", *counter.lock().unwrap());
}
|
解释:
- 该示例展示了如何使用线程、通道和互斥锁实现并发编程。
5. 总结
Rust 提供了强大的并发编程支持,包括线程、消息传递和共享状态。通过线程,我们可以并行执行代码;通过消息传递,线程可以安全地通信;通过共享状态,线程可以安全地共享数据。掌握这些工具是编写高效、安全的并发程序的关键。