如何简化该Rust Tauri侧进程通信代码,消除冗余Arc、Mutex提升可读性
冗余项说明
原代码里大部分的Arc、Mutex和多余的clone都不是必要的,冗余来源主要是对Tauri和Tokio原生类型的线程安全特性不熟悉:
- Tauri的
Window类型本身已经是线程安全的引用计数结构,实现了Clonetrait,不需要额外套Arc<Mutex>包装 - Tokio的
broadcast::Sender本身支持多线程安全clone,不需要套Mutex - 大量重复的
clone操作只是为了满足闭包捕获的所有权要求,完全可以简化 - 唯一需要保留的
Arc<Mutex>只有包装子进程Child的部分,因为写入子进程stdin需要可变引用,且需要跨多个闭包共享
惯用写法优化后的代码
use tauri::{Window, Wry, command}; use tauri::api::process::{Command, CommandEvent}; use tokio::sync::broadcast; use std::sync::Arc; use tokio::sync::Mutex; #[command] fn spawn(param: String, window: Window<Wry>) { // 启动子进程,只有Child需要Arc<Mutex>包装,因为要跨线程写stdin let (mut rx, child) = Command::new_sidecar("bin") .expect("failed to create binary command") .args([param]) .spawn() .expect("Failed to spawn sidecar"); let child = Arc::new(Mutex::new(child)); // 全局kill信号通道,复用给两个异步任务 let (kill_tx, mut kill_rx1) = broadcast::channel(1); let kill_rx2 = kill_tx.subscribe(); // 任务1:处理子进程stdout/stderr输出 let win_out = window.clone(); tauri::async_runtime::spawn(async move { loop { tokio::select! { _ = kill_rx1.recv() => return, Some(event) = rx.recv() => match event { CommandEvent::Stdout(line) => { let data = decode_and_xor(line); println!("Data from bin: {}", data); win_out.emit("from_bin", data).expect("failed to emit message"); } CommandEvent::Stderr(line) => { println!("Fatal error bin: {}", line); win_out.emit("bin_fatal_error", line).expect("failed to emit message"); } _ => {} } } } }); // 任务2:处理前端发往子进程的消息 let win_in = window.clone(); let child_clone = child.clone(); tauri::async_runtime::spawn(async move { // 监听前端发送到子进程的消息 let _to_bin_handler = win_in.listen("to_bin", move |event| { let payload = event.payload().unwrap().to_string(); let encrypted = xor_and_encode(payload) + "\n"; println!("Data to send: {}", payload); child_clone.blocking_lock().write(encrypted.as_bytes()).expect("could not write to child"); }); // 等待kill信号结束当前任务 let _ = kill_rx2.recv().await; }); // 统一监听kill_bin事件,发信号结束两个后台任务 window.listen("kill_bin", move |event| { let _ = kill_tx.send(true); window.unlisten(event.id()); }); }
优化要点
- 移除了所有对
Window的额外包装,直接用原生clone传递,代码中所有window.lock().unwrap()的冗余调用都被删除,核心逻辑直接暴露 - 复用同一个kill信号通道,删除了原代码中两套重复的通道逻辑,减少了冗余代码
- 只保留了子进程
Child的Arc<Mutex>包装,是唯一必要的锁操作 - 调整了代码结构,按功能块划分,核心逻辑(子进程输出处理、子进程输入处理、kill逻辑)清晰分离,没有多余的锁操作干扰阅读
内容的提问来源于stack exchange,提问作者asd12tgzxvbgt
相关产品推荐
相关产品推荐

