You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tokio实现I3状态栏生成器:循环执行与SIGUSR1触发可变函数

解决I3状态栏生成器的Tokio并发与可变借用冲突问题

核心问题分析

你遇到的可变变量借用冲突,本质是Rust的借用规则不允许同一时间存在多个&mut self引用。Tokio的join!会并发执行两个异步任务,主循环和信号监听任务都需要修改结构体状态,直接使用&mut self必然触发编译错误。

解决方案:用线程安全的内部可变性容器重构

通过Arc<Mutex>(或Arc<RwLock>)包装需要共享修改的状态,实现多任务下的安全访问,同时符合Tokio的异步模型。

步骤1:调整结构体定义

将可变字段用Arc<Mutex>包裹,实现线程安全的共享所有权与内部可变性:

use std::sync::{Arc, Mutex};
use tokio::signal::unix::{signal, SignalKind};
use tokio::time::{sleep, Instant, Duration};

// 假设Plug是你的插件 trait
pub trait Plug {
    fn prefix(&mut self) -> String;
    fn plug_out(&mut self) -> String;
}

pub struct BarGenerator {
    speed: u64,
    is_powerline: bool,
    end_pw_line: bool,
    fg: String,
    bg: String,
    seperator: String,
    pw_colors: Vec<String>,
    pw_color_num: Arc<Mutex<usize>>, // 包装可变状态
    function_list: Arc<Mutex<Vec<Box<dyn Plug>>>>, // 包装可变插件列表
}

步骤2:重构run_bar方法

拆分异步任务,用锁安全访问共享状态,同时运行主循环与SIGUSR1监听:

pub async fn run_bar(self) {
    let interval = Duration::from_millis(self.speed);
    // 捕获不可变状态
    let pw_colors = self.pw_colors;
    let is_powerline = self.is_powerline;
    let end_pw_line = self.end_pw_line;
    let fg = self.fg;
    let bg = self.bg;
    let seperator = self.seperator;
    // 捕获锁包裹的可变状态
    let pw_color_num = self.pw_color_num;
    let function_list = self.function_list;

    // 提取状态栏生成逻辑,通过锁访问可变状态
    let generate_status = move || {
        let mut statusline = String::new();
        // 获取锁访问pw_color_num
        let mut pw_num_guard = pw_color_num.lock().unwrap();
        let mut pw_color_num_val = *pw_num_guard;
        let og_pw_num = pw_color_num_val;

        // 获取锁访问插件列表
        let mut funcs_guard = function_list.lock().unwrap();
        let fn_list_size = funcs_guard.len();

        for (i, x) in funcs_guard.iter_mut().enumerate() {
            let og_pw_num_2 = pw_color_num_val;
            if pw_color_num_val == pw_colors.len() {
                pw_color_num_val = og_pw_num;
            }

            if is_powerline {
                if i == 0 {
                    statusline.push_str(&format!(
                        "<span foreground='{}'></span>",
                        pw_colors[pw_color_num_val]
                    ));
                } else if pw_color_num_val != 0 {
                    statusline.push_str(&format!(
                        "<span background='{}' foreground='{}'></span>",
                        pw_colors[pw_color_num_val],
                        pw_colors[pw_color_num_val - 1]
                    ));
                } else {
                    statusline.push_str(&format!(
                        "<span background='{}' foreground='{}'></span>",
                        pw_colors[pw_color_num_val],
                        pw_colors[og_pw_num_2 - 1]
                    ));
                }
                statusline.push_str(&format!(
                    "<span background='{}' foreground='{}'> ",
                    pw_colors[pw_color_num_val], fg
                ));
            }

            statusline.push_str(x.prefix().as_str());
            statusline.push_str(x.plug_out().as_str());

            if is_powerline {
                statusline.push_str(" </span>");
                if i == fn_list_size - 1 && end_pw_line {
                    statusline.push_str(&format!(
                        "<span foreground='{}' background='{}'></span>",
                        pw_colors[pw_color_num_val], bg
                    ))
                }
                pw_color_num_val += 1;
            }

            if i != fn_list_size - 1 && !is_powerline {
                statusline.push_str(&seperator)
            }
        }

        // 更新锁中的pw_color_num值
        *pw_num_guard = pw_color_num_val;
        println!("{}", statusline);
    };

    // 主循环任务:定时生成状态栏
    let main_loop = async move {
        let mut next_time = Instant::now() + interval;
        loop {
            generate_status();
            sleep(next_time - Instant::now()).await;
            next_time += interval;
        }
    };

    // SIGUSR1监听任务:收到信号立即更新状态栏
    let sigusr1_listener = async move {
        let mut sigusr1 = signal(SignalKind::user_defined1()).unwrap();
        loop {
            match sigusr1.recv().await {
                Some(()) => generate_status(),
                None => eprintln!("SIGUSR1信号流终止"),
            }
        }
    };

    // 并发执行两个任务
    tokio::join!(main_loop, sigusr1_listener);
}

关键修改说明

  1. 内部可变性容器:Arc<Mutex>允许在多个异步任务中安全共享和修改状态,Arc提供线程安全的所有权共享,Mutex保证同一时间只有一个任务能修改内部数据。
  2. 任务拆分:将主循环与信号监听拆分为独立异步任务,通过tokio::join!并发执行,符合Tokio异步调度模型。
  3. 锁的使用:每次生成状态栏时,通过lock()获取状态的访问权限,用完自动释放锁,避免长期阻塞。

可选优化

如果插件列表的读操作远多于写操作,可以用Arc<RwLock>替代Arc<Mutex>,允许多个任务同时读取状态,提升并发性能。

内容的提问来源于stack exchange,提问作者tux7k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 05:50:05