Rust学习记录 -> 线程池的实现和改进


前言

在经过前期对闭包,互斥器,智能指针,线程,通道和Tcp服务的学习后,笔者开始试着跟着视频写一个简单的web server,其中实现了线程池,让整个进程更加合理的分配线程。
以下是我在实现过程中的一些心得和遇到的问题。


web server实现

接下来我会直接给出代码并在注释中进行解释,以及对编写过程中遇到的问题进行讨论。

lib.rs:

use std::thread;
//导入线程包
use std::sync::mpsc;
//多个生产者单个消费者
use std::sync::Arc;
//智能指针
use std::sync::Mutex;
//互斥器

type Job = Box<dyn FnOnce() + Send + 'static>;
//定义传回闭包的特征

enum Message {
    
    
//使用枚举类型定义在web server中的消息类型
    NewJob(Job),
    //收到的任务
    Terminate,
    //中断消息
}

struct Worker {
    
    
    id:usize,
    //worker的编号
    thread:Option<thread::JoinHandle<()>>,
    //worker中内置的线程
}

impl Worker {
    
    
//定义worker的方法
    fn new(id:usize,recevier:Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
    
    
    //定义worker的初始化方法
    //由于worker是内置的,所以默认为私有类型
        let thread = thread::spawn(move || {
    
    
        //定义内置线程
            loop {
    
    
            //让worker的内置线程不断循环,始终保持运行态
                //let job = recevier.lock().unwrap().recv().unwrap();
                //println!("Worker {} get a job!",id);
                //job();
                //如上是无线程池配置时的线程任务
                //无论有无收到任务,线程都是开启的,并且由recv()函数的特性,线程也一直处于阻塞态
                //这是一种非常浪费资源的安排,所以引入drop特性,对线程进行数量限制以及终止,释放空闲资源
                //match recevier.lock().unwrap().recv().unwrap() {
    
    
                let message = recevier.lock().unwrap().recv().unwrap();
                //疑问:当如上直接match时,进程会卡在某一个worker上,为什么?
                match message {
    
    
                //在这里对接收到的消息进行类型匹配
                        Message::NewJob(job) => {
    
    
                        //当接收到任务时
                        println!("worker {} get a job!",id);
                        job()
                        //从Option Some(Job)中取出Job并执行闭包中的任务
                    },
                    Message::Terminate => {
    
    
                    //接收到中断消息
                       println!("worker {} terminat!",id);
                        break;
                        //终止线程
                    },
                }
            }
        });

        Worker {
    
    
        //返回worker结构体
            id,
            thread:Some(thread),
        }
    }
}

pub struct ThreadPool {
    
    
//定义线程池结构体具体结构
    workers: Vec<Worker>,
    //定义内置类型为worker的vec容器
    sender: mpsc::Sender<Message>,
    //为线程池配置发送端,以便对worker进行调度
}

impl ThreadPool {
    
    
//为线程池结构体定义方法
    pub fn new(size:usize) -> ThreadPool {
    
    
    //定义初始化函数
        let (sender, recevier) = mpsc::channel();
        //创建通道的发送端和接收端
        let recevier = Arc::new(Mutex::new(recevier));
        //将recevier放入智能指针Arc中并使用Mutex互斥器,保证线程安全
        let mut workers = Vec::with_capacity(size);
				//根据线程池中预设的线程总数定义vec容器
        for id in 0..size {
    
    
        //将worker放入提前定义的ec容器中
            workers.push (Worker::new(id,Arc::clone(&recevier)));
			//注意要使用clone
        ThreadPool {
    
    
        //返回线程池结构体
            workers,
            sender,
        }
    }

    pub fn execute<F>(&self, f:F) where F: FnOnce() + Send + 'static {
    
    
    //定义 execute 方法
        let job = Box::new(f);
		//传入的任务放入box智能指针中
        self.sender.send(Message::NewJob(job)).unwrap();
        //将任务发送进通道等待recv接收
    }
}

impl Drop for ThreadPool {
    
    
//为线程池结构体配置drop特性
    fn drop(&mut self) {
    
    
    //首先向线程发送drop消息
        for _ in &self.workers {
    
    
            self.sender.send(Message::Terminate).unwrap();
        }

        for worker in &mut self.workers{
    
    
        //等待线程结束
            if let Some(thread) = worker.thread.take() {
    
    
                thread.join().unwrap();
            }
        }
    }
}
                                                                                                                        


                                                                                                               

main.rs:

use std::thread;
use std::time::Duration;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::fs;
use std::net::TcpListener;
use mylib::ThreadPool;

fn handle_connection(mut stream: TcpStream) {
    
    
//定义响应方法
    let mut buffer = [0; 512];
    stream.read(&mut buffer).unwrap();

    let get = b"GET / HTTP/1.1\r\n";
    let sleep = b"GET /sleep HTTP/1.1\r\n";
	//定义检测前缀
    let (status_line, filename) = if buffer.starts_with(get) {
    
    
    //定义返回状态和返回页面文件名
        ("HTTP/1.1 200 OK\r\n\r\n", "test.html")
    } else if buffer.starts_with(sleep) {
    
    
        thread::sleep(Duration::from_secs(25));
        ("HTTP/1.1 200 OK\r\n\r\n", "test.html")
    } else {
    
    
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
    };

    let content = fs::read_to_string(filename).unwrap();
    let response = format!("{}{}",status_line,content);
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
    //thread::sleep(Duration::from_secs(10));
}

fn main() {
    
    
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
    let pool = ThreadPool::new(4);

    for stream in listener.incoming().take(4) {
    
    
        let stream = stream.unwrap();
        pool.execute(move || {
    
    
            handle_connection(stream)
        });
    }
}

总结

以上代码就是我在学习web server实例时的心得与一些体会,以及遇到的问题。上文中提到的问题暂时还没有得到解决,所以笔者希望能有读者大牛在看完后可以不吝赐教解惑,感谢阅读。

猜你喜欢

转载自blog.csdn.net/weixin_45704680/article/details/121180702