前言
阅读耗时四分钟
目录
一、概念
安全高效的处理并发是 Rust 诞生的目的之一,主要解决的是服务器高负载承受能力。
并发(concurrent
)的概念是指程序不同的部分独立执行,这与并行(parallel
)的概念容易混淆,并行强调的是"同时执行"。
并发往往会造成并行。
Concurrent
:程序的不同部分之间独立的执行
Parallel
: 程序的不同部分同时运行
本文中的并发泛指 concurrent
和parallel
。
- 多线程问题
- 竞争状态,线程以不一致的顺序访问数据或资源
- 死锁,两个线程彼此等待对方使用完所持有的资源,线程无法继续
- 只在某些情况下发生的Bug,很难可靠地复制现象和修复
二、线程
实现
主要通过thread::spawn
创建线程
use std::time::Duration;
use std::thread;
fn main() {
thread::spawn(||{
for i in 1..10{
println!("number {} from spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5{
println!("number {} from main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}
输出
number 1 from main thread!
number 1 from spawned thread!
number 2 from spawned thread!
number 2 from main thread!
number 3 from main thread!
number 3 from spawned thread!
number 4 from spawned thread!
number 4 from main thread!
number 5 from spawned thread!
主线程执行完了,程序执行完了。如果想要和java
一样,作为守护线程,可以用join
可以这样改
use std::time::Duration;
use std::thread;
fn main() {
let handle = thread::spawn(||{
for i in 1..10{
println!("number {} from spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
// handle.join().unwrap(); //会等handle线程执行完才会执行下面的
for i in 1..5{
println!("number {} from main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap(); //主线程执行完了,会等待handle线程执行完。
}
值得一提的是 move
,转交所有权,之前说过就不介绍了,直接来个简单例子
use std::thread;
fn main(){
let v = vec![1, 2, 3];
let handle = thread::spawn(move ||{
println!("Here's a vector: {:?}", v); // 所有权已经给线程里咯
});
//drop(v); 这行会报错,因为所有权已经通过move移交到线程里了。
handle.join().unwrap();
}
三、消息发送
主要是通过Rust
提供的channel
,但是一旦将值的所有权移交给了channel
,就无法使用了。
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move||{
let val = String::from("hi");
tx.send(val).unwrap();
//println!("val is {}", val); //这里val会报错,因为所有权已经移交出去了,通过send
});
let received = rx.recv().unwrap(); //阻塞等待
println!("Got {}" , received);
}
四、共享状态并发
主要是介绍,体现Mutex
互斥锁的作用。
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}//自动会解锁
println!("m = {:?}", m);
}
多线程
fn main() {
let counter = Rc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10{
let counter = Rc::clone(&counter);
let handle = thread::spawn(move || { //这里会报错,因为Rc不支持多线程用法,必须要实现Send的Trait才支持
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles{
handle.join().unwrap();
}
println!("Result : {}", *counter.lock().unwrap());
}
利用Arc
进行多线程原子引用计数,修改为如下
use std::sync::{Mutex,Arc};
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 Trait
的类型可在线程间转移所有权,但是Rc<T>
没有实现Send
,所以Rc<T>
只能用于单线程场景。
实现了Sync
的类型可以安全的被多个线程引用,基础类型都是Sync
,但是Rc<T>
、RefCell<T
>、Cell<T>
不是Sync
,Mutex<T>
是Sync
的。
六、Unsafe Rust
在Unsafe Rust
中有四种情况不会被编译器进行内存安全检查
- 解引用原始指针
- 调用
unsafe
函数或者方法 - 访问或者修改可变的静态变量
- 实现
unsafe trait
使用 unsafe
关键字可以切换到 unsafe rust
,开启一个块,里面放着unsafe
代码。但是unsaf
e并没有关闭借用检查或者停用其它安全检查。
任何内存安全相关的错误必须留在unsafe
块里,尽可能隔离unsafe
代码,将其封装在安全的抽象里。
主要是编译器无法保证内存安全,通过人为代码来保证不简单,通过unsafe
显示标记,定位问题将会快一点。
原始指针
fn main() {
let mut num = 5;
let r1 = &num as * const i32;//可变的原始指针
let r2 = &mut num as * mut i32;//不可变得原始指针
println!("r1 = {}", *r1); //解引用错误,报错,必须要在unsafe中解引用原始指针
let address = 0x012345usize;
let r = address as * const i32;
}
修改如下
fn main() {
let mut num = 5;
let r1 = &num as * const i32;//可变的原始指针
let r2 = &mut num as * mut i32;//不可变得原始指针
unsafe{
println!("r1 = {}", *r1);
}
let address = 0x012345usize;
let r = address as * const i32;
}
既然使用原始指针有点不安全,为什么要使用,主要作用还是为了和C
语言进行接口交互。
调用unsafe函数或者方法
fn main() {
dangerous();//报错,提示必须要在unsafe块中调用
}
unsafe fn dangerous(){}
修改如下
fn main() {
unsafe{
dangerous();
}
}
unsafe fn dangerous(){}
从其他语言调用Rust
函数,也是一种不安全操作
#[no_mangle] //禁止编译器改名
pub extern "C" fn call_from_c(){
println!("Just called a rust func form C !")
}
访问或者修改可变的静态变量
static HELLO_WORLD:&str = "Hello World";
static mut COUNTER :u32 = 0;
fn add_to_count(inc: u32){
unsafe{
COUNTER += inc;
}
}
fn main() {
add_to_count(3);
println!("{}", HELLO_WORLD);
unsafe{
println!("COUNTER:{}", COUNTER);
}
}
实现unsafe trait
当某个trait
中至少一个方法拥有编译器无法校验的不安全因素时,就称这个trait
是不安全的
unsafe trait Foo {
}
unsafe impl Foo for i32 {
}
七、线程实例
单线程服务器
use std::{
fs,
io::{Read, Write},
net::{TcpListener, TcpStream},
};
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
//依次处理每个请求
for stream in listener.incoming() {
let _stream = stream.unwrap();
handle_connection(_stream);
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("Request:{}", String::from_utf8_lossy(&buffer[..]));
let get = b"GET / HTTP/1.1\r\n";
let(status_line, filename) = if buffer.starts_with(get) {
("HTTP/1.1 200 OK\r\n\r\n{}", "hello.html")
}else{
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
};
let contents = fs::read_to_string(filename).unwrap();
let response = format!("{}{}", status_line, contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
多线程服务器
lib.rs
use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
pub struct ThreadPool{
workers:Vec<Worker>,
sender: mpsc::Sender<Job>,
}
impl ThreadPool{
///Crete a new ThreadPool
pub fn new(size: usize) -> ThreadPool{
assert!(size > 0);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool{workers, sender}
}
pub fn execute<F>(&self, f:F)
where
F: FnOnce() + Send + 'static,{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
struct Worker{
id:usize,
thread: thread::JoinHandle<()>,
}
type Job = Box<dyn FnBox + Send + 'static>;
impl Worker {
fn new(id:usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker{
let thread = thread::spawn(move || loop {
while let Ok(job) = receiver.lock().unwrap().recv(){
println!("Worker {} got a job; excuting.", id);
// (*job)();
job.call_box();
}
});
Worker { id, thread }
}
}
trait FnBox{
fn call_box(self: Box<Self>);
}
impl <F: FnOnce()> FnBox for F {
fn call_box(self: Box<F>) {
(*self)()
}
}
main.rs
use std::{
thread,
fs,
io::{Read, Write},
net::{TcpListener, TcpStream},
};
use web::ThreadPool;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
let pool = ThreadPool::new(4);
//依次处理每个请求
for stream in listener.incoming() {
let _stream = stream.unwrap();
pool.execute(||{
handle_connection(_stream);
});
thread::spawn(||{});
}
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer = [0; 512];
stream.read(&mut buffer).unwrap();
println!("Request:{}", String::from_utf8_lossy(&buffer[..]));
let get = b"GET / HTTP/1.1\r\n";
let(status_line, filename) = if buffer.starts_with(get) {
("HTTP/1.1 200 OK\r\n\r\n{}", "hello.html")
}else{
("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
};
let contents = fs::read_to_string(filename).unwrap();
let response = format!("{}{}", status_line, contents);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}