• 如何在Rust中实现事件循环模式
  • 发布于 2个月前
  • 154 热度
    0 评论
  • 苒青绾
  • 0 粉丝 25 篇博客
  •   
事件循环是编程中的一个基本概念,用作管理异步操作和确保系统响应的机制。它的功能是不断检查程序中的新事件或消息,根据需要处理它们,然后继续检查更多事件或消息。

这种模式对于处理输入/输出操作、网络通信和用户交互等任务至关重要,从而不会阻塞其他代码的执行。事件循环的重要意义在于它能够有效地并发处理多个任务,而不依赖于传统的同步方法,后者可能导致性能瓶颈和应用程序无响应。事件循环使程序在事件发生时异步的处理事件,从而可以更平滑和更有效的执行程序,使其成为现代软件开发中的关键组件,特别是在必须响应的环境中。

事件循环的一些用例:
图形用户界面(GUIs):处理用户交互,如鼠标点击和按键。
网络:管理异步I/O操作,例如传入和传出网络请求。
游戏开发:处理游戏状态、渲染和处理玩家输入。
物联网设备:异步响应传感器数据和外部命令。

在Rust中实现事件循环模式
下面是Rust中事件循环的一个示例实现。
我们首先需要在Cargo.toml文件中加入依赖项:
[dependencies]
crossbeam = "0.8.4"
strum = "0.26.2"
strum_macros = "0.26.4"
crossbeam:用于并发编程的工具
strum:strum是一组宏和trait,在Rust中可以更容易地处理枚举和字符串。
strum_macros:只包含用于strum箱子的派生宏
然后在src/main.rs文件中写入以下代码。

首先定义一个事件枚举:
#[derive(Clone, Debug, PartialEq, Eq, Hash, Display, EnumString)]
pub enum Event {
    Dummy,
    TestEvent,
}
Event是一个枚举,表示事件循环可以处理的事件类型。Display和EnumString派生用于更容易地处理和转换枚举值。

定义一个类型别名Payload:
pub type Payload = Vec<u8>;
Payload被定义为Vec<u8>的类型别名,表示与事件关联的数据。

定义一个Trait Handler:
pub trait Handler: Send + Sync {
    fn handle_event(&self, event: Event, payload: Payload);
}
Handler是任何事件处理程序都必须实现的trait。它需要一个方法handle_event,该方法接受Event和Payload。由于事件循环在自己的线程中运行,并且可能跨不同线程与多个Handler程序交互,因此Handler程序可能需要在线程之间移动。通过标记Send Trait,可以确保Handler实现程序可以跨线程边界传输,从而使事件循环在多线程上下文中变得安全和健壮。

Handler程序存储在一个共享的Arc<Mutex<HashMap<Event, Vec<Arc<dyn Handler>>>>>中。Arc(原子引用计数)允许多个线程共享处理程序的所有权,而无需克隆它们。通过标记Sync Trait,可以确保多个线程可以安全地保存对同一Handler程序的引用。这意味着对Handler程序状态的任何读访问都是线程安全的。

定义一个结构体Listener:
#[derive(Clone)]
pub struct Listener {
    pub event: Event,
    pub handler: Arc<dyn Handler>,
}
Listener结构体将事件绑定到实现handler的处理程序,从而允许为每个事件添加多个处理程序。

定义一个Dispatcher结构体:
pub struct Dispatcher {
    tx: Sender<(Event, Payload)>,
    rx: Receiver<(Event, Payload)>,
    // 事件注册表
    registry: Arc<Mutex<HashMap<Event, Vec<Arc<dyn Handler>>>>>,
}

impl Dispatcher {
    pub fn new() -> Self {
        let (tx, rx) = unbounded();
        Dispatcher {
            tx,
            rx,
            registry: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    // 向注册表中注册事件及其对应的处理程序
    pub fn register_handler(&mut self, event: Event, handler: Arc<dyn Handler>) {
        let mut registry = self.registry.lock().unwrap();
        registry.entry(event).or_insert_with(Vec::new).push(handler);
    }
    // 堆代码 duidaima.com
    // 向通道发送事件
    pub fn trigger_event(&self, event: Event, payload: Payload) {
        self.tx.send((event, payload)).unwrap();
    }

    // 接收通道中的事件并进行处理
    pub fn start(&self) {
        let registry = Arc::clone(&self.registry);
        let rx = self.rx.clone();

        thread::spawn(move || loop {
            if let Ok((event, payload)) = rx.recv() {
                let registry = registry.lock().unwrap();
                if let Some(handlers) = registry.get(&event) {
                    for handler in handlers {
                        handler.handle_event(event.clone(), payload.clone());
                    }
                }
            }
        });
    }
}
Dispatcher保存事件通道的发送方和接收方以及线程安全的Handler实现集合。

测试
我们定义多个事件处理器:
pub struct TestEventHandler;

impl Handler for TestEventHandler {
    fn handle_event(&self, event: Event, payload: Payload) {
        let data = String::from_utf8(payload).unwrap();
        let message = format!("{} => {}", event, data);

        println!("TestEvent: {}", message);
    }
}

pub struct DBTestEventHandler;

impl Handler for DBTestEventHandler {
    fn handle_event(&self, event: Event, payload: Payload) {
        let data = String::from_utf8(payload).unwrap();
        let message = format!("{} => {}", event, data);

        // 将数据持久化到db中
        println!("Data: {} saved on DB!", message);
    }
}
在main函数中写入以下代码:
fn main() {
    let mut event_loop = Dispatcher::new();

    event_loop.register_handler(Event::TestEvent, Arc::new(TestEventHandler));
    event_loop.register_handler(Event::TestEvent, Arc::new(DBTestEventHandler));

    // 启动事件循环
    event_loop.start();

    loop {
        println!("Give me some input, type 'exit' to quit");

        let mut input = String::new();

        std::io::stdin()
            .read_line(&mut input)
            .expect("Error during input");

        let input = input.trim();

        if input == "exit" {
            break;
        }

        let mut split = input.split_whitespace();
        let name_data = (
            split.next().unwrap_or_default().to_string(),
            split.next().unwrap_or_default().to_string(),
        );

        let event = Event::from_str(&name_data.0).unwrap_or_else(|_| Event::Dummy);
        event_loop.trigger_event(event, name_data.1.as_bytes().to_vec());
    }
}
运行结果如下:
Give me some input, type 'exit' to quit
exit

用户评论