rust多线程,和erlang非常类似,都使用spawn创建一个线程。例如一个erlang的例子
-module(thread_example).
-export([start/0]).
% 定义要在新线程中执行的函数
print_message() ->
io:format("Hello from the new thread~n").
% 启动函数
start() ->
% 使用spawn函数创建一个新的线程,并传递print_message函数
spawn(fun print_message/0),
ok.
对比一下rust,可以发现erlang和rust及其相似,rust学习了erlang。
1. rust创建线程
rust使用thread包负责创建线程,spawn定义如下:
pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static,
一个例子代码:
let mut handles = vec![];
for i in 0..NTHREADS {
handles.push(thread::spawn(move || { println!("this is thread number {}", i);}));
}
println!("waiting for every thread");
for handle in handles {
handle.join();
}
例如说这个例子,创建10个线程,每个线程打印一个数字。
- 注意spawn使用了闭包,打印了一个外部变量,而spawn的定义F:FnOnce(),所以在闭包前使用了move,将i的所有权转给闭包。
- spawn返回JoinHandler,有三个方法
- pub fn is_finished(&self) -> bool,判断该线程是否运行完毕
- pub fn join(self) -> Result<T, Err>,调用该方法的线程会等到handle持有线程运行完毕
- pub fn thread(&self) -> &Thread,返回handler对应的thread
1.1 thread::Tread获取线程的id和名字
use std::thread;
// This is the `main` thread
fn main() {
println!("{:?}", thread::current().id());
println!("{:?}", thread::current().name());
}
返回:
ThreadId(1)
Some("main")
let handler = thread::spawn(move || {println!("hello")});
prantln!("{:?}", handler.thread().id());
prantln!("{:?}", handler.thread().name());
返回:
ThreadId(2)
None
1.2 thread::Builder(如何给thread命名)
let builder = thread::Builder::new()
.name("foo".into());
let handler = builder.spawn(|| {
println!("{:?}", thread::current().name());
}).unwrap();
handler.join().unwrap();
1.3 线程中如何传递共享参数
let v = Arc::new(Mutex::new(vec![1,2,3]));
let mut handlers = vec![];
for i in 0..3 {
let clone_v = v.clone();
handlers.push(thread::spawn(move || {clone_v.lock().unwrap().push(i)}));
}
for handler in handlers {
let _ = handler.join();
}
println!("{:?}", v.lock().unwrap());
共享参数使用Arc只能指针来传递,Arc是个引用,其指向的值并未被复制。在操作的时候使用Mutex来完成同步。
1.4 thread::park和thread:unpark
use std::thread;
use std::sync::{Arc, atomic::{Ordering, AtomicBool}};
use std::time::Duration;
let flag = Arc::new(AtomicBool::new(false));
let flag2 = Arc::clone(&flag);
let parked_thread = thread::spawn(move || {
// We want to wait until the flag is set. We *could* just spin, but using
// park/unpark is more efficient.
while !flag2.load(Ordering::Acquire) {
println!("Parking thread");
thread::park();
// We *could* get here spuriously, i.e., way before the 10ms below are over!
// But that is no problem, we are in a loop until the flag is set anyway.
println!("Thread unparked");
}
println!("Flag received");
});
// Let some time pass for the thread to be spawned.
thread::sleep(Duration::from_millis(10));
// Set the flag, and let the thread wake up.
// There is no race condition here, if `unpark`
// happens first, `park` will return immediately.
// Hence there is no risk of a deadlock.
flag.store(true, Ordering::Release);
println!("Unpark the thread");
parked_thread.thread().unpark();
parked_thread.join().unwrap();
注意这里调用thread::park()阻塞的是当前线程,而接触阻塞调用的是handler.thread().unpark()。
另外还有thread::park_timeout()方法,对阻塞设置超时。
小结
thread包封装了比较简洁的方法,提供线程相关的操作,thread包提供了join和park方法完成线程之间的协作;构造了JoinHandler、Builder、Thread、ThreadId等结构,有序的组合除了thread操作。学习thread包,应把doc文档好好看一遍,消化第一手知识,而不要只看文章,看别人消化了加工出的知识。
网友评论