thread in Rust

本文介绍Rust中的多线程并发。Rust提供了std::thread::spawn来创建线程。

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

输出如下:

manu-Inspiron-5748 RBE/thread ‹master*› » cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/thread`
hi number 1 from main thread!
hi number 1 from spawned thread!
hi number 2 from main thread!
hi number 2 from spawned thread!
hi number 3 from main thread!
hi number 3 from spawned thread!
hi number 4 from main thread!
hi number 4 from spawned thread!
hi number 5 from spawned thread!
hi number 6 from spawned thread!
hi number 7 from spawned thread!
hi number 8 from spawned thread!
hi number 9 from spawned thread!

我们使用了join来等待线程退出,由于主进程和线程都有sleep 1毫秒的行为,如果没有主进程调用的join,那么随着主进程的退出,线程也退出了,后面的语句也不会打印出来。

use std::thread ;
use std::time::Duration;
fn main()
{
let _handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from main thread!", i);
thread::sleep(Duration::from_millis(1));
}
//handle.join().unwrap();
}

运行结果如下:

hi number 1 from main thread!
hi number 1 from spawned thread!
hi number 2 from main thread!
hi number 2 from spawned thread!
hi number 3 from main thread!
hi number 3 from spawned thread!
hi number 4 from spawned thread!
hi number 4 from main thread!
hi number 5 from spawned thread!

join: 等待线程退出

std::thread::spawn 函数的返回类型是JoinHandle, 我们可以在JoinHandle上调用join方法,等待线程退出。

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

上述代码中,主进程会等待线程退出之后,然后打印主进程的信息:

hi number 1 from spawned thread!
hi number 2 from spawned thread!
hi number 3 from spawned thread!
hi number 4 from spawned thread!
hi number 5 from spawned thread!
hi number 6 from spawned thread!
hi number 7 from spawned thread!
hi number 8 from spawned thread!
hi number 9 from spawned thread!
hi number 1 from main thread!
hi number 2 from main thread!
hi number 3 from main thread!
hi number 4 from main thread!

JoinHandle的join函数,原型如下:

pub fn join(self) -> Result<T>

其返回类型是Result类型。 因此在上面的示例中,我们需要调用unwrap或者expect来处理返回值,如果去除掉unwrap(),就会返回如下的警告:

warning: unused `std::result::Result` that must be used
--> src/main.rs:14:5
|
14 |     handle.join();
|     ^^^^^^^^^^^^^^
|
= note: `#[warn(unused_must_use)]` on by default
= note: this `Result` may be an `Err` variant, which should be handled

定制线程

我们通过std::thread::spawn 可以创建一个线程,而这种线程,一般是典型的线程,线程没有名字,而且线程栈大小为默认的2MB,如果像定制特殊的线程,则需要std::thread::Builder。

目前来讲,有两个参数可以设定:

  • name:设定线程的名字
  • stack_size: 制定线程的栈大小
let builder = Builder::new().name(thread_name).stack_size(size);
let child = builder.spawn(move || {
println!("I am child {}", id);
thread::sleep(Duration::from_secs(100));
println!("I am child {}", current().name().unwrap());
}).unwrap();

注意Builder::new之后,还是需要调spawn函数,来唤起线程。

pub fn spawn<F, T>(self, f: F) -> Result<JoinHandle<T>>

主进程为了调用join,需要对Builder::spawn的返回值做unwrap处理,方能调用join函数,否则:

error[E0599]: no method named `join` found for enum `std::result::Result<std::thread::JoinHandle<()>, std::io::Error>` in the current scope
--> src/main.rs:21:15
|
21 |         child.join().unwrap();
|               ^^^^ method not found in `std::result::Result<std::thread::JoinHandle<()>, std::io::Error>`

整体示例代码如下:

use std::thread;
use std::thread::{Builder, current} ;
use std::time::Duration;
fn main()
{
let mut v = vec![];
for id in 0..5 {
let thread_name = format!("thread-{}", id);
let size: usize = 8192 ;
let builder = Builder::new().name(thread_name).stack_size(size);
let child = builder.spawn(move || {
println!("I am child {}", id);
thread::sleep(Duration::from_secs(100));
println!("I am child {}", current().name().unwrap());
}).unwrap();
v.push(child)
}
for child in v{
child.join().unwrap();
}
for i in 1..5 {
println!("hi number {} from main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}

输出如下:

I am child 0
I am child 1
I am child 2
I am child 3
I am child 4
I am child thread-1
I am child thread-3
I am child thread-0
I am child thread-2
I am child thread-4
hi number 1 from main thread!
hi number 2 from main thread!
hi number 3 from main thread!
hi number 4 from main thread!

我们通过top -H -p [PID]来查看线程的名字是否生效:

使用move闭包

一般来讲,move闭包总是伴随着thread::spawn。我们先看下面的示例:

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

上述代码执行会出现报错:

error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
--> src/main.rs:6:32
|
6 |     let handle = thread::spawn(|| {
|                                ^^ may outlive borrowed value `v`
7 |         println!("Here's a vector: {:?}", v);
|                                           - `v` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:6:18
|
6 |       let handle = thread::spawn(|| {
|  __________________^
7 | |         println!("Here's a vector: {:?}", v);
8 | |     });
| |______^
help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
|
6 |     let handle = thread::spawn(move || {
|                                ^^^^^^^
error: aborting due to previous error

线程只是要打印v的内容,闭包会尝试borrow v,但是有个致命的问题,即Rust推断不出来,线程要运行多久,以及v这个变量引用是否一直有效。考虑如下场景:

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

如果Rust允许执行上述代码,新创建的线程,很可能在主线程drop之后,调用println!,这种情况下,v已经失效了。这就不妙了。

通过在闭包之前,添加move关键字,我们会强制闭包获得他需要的值的所有权,而不仅仅是机遇Rust推导来获得值的借用。

修改正确之后的代码如下:

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();
}
Bean Li
我还没有学会写个人说明!
上一篇

NASA约翰逊航天中心迎来猎户座训练模拟器

下一篇

美CDC发警告:药物服用过量致死人数在大流行期间出现激增

你也可能喜欢

评论已经被关闭。

插入图片