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

Rust单线程异步场景下如何避免使用锁处理共享可变引用

解决方案

Rust 的可变引用独占规则是编译期静态约束,不会根据运行时是否为单线程环境做特殊适配,因此即使逻辑上不存在并发修改风险,编译器也会拒绝同时存在多个可变引用的写法。针对单线程Tokio场景,你可以完全不用锁,通过以下两种方式实现需求:

方案1:使用RefCell实现单线程内部可变性(性能最优)

std::cell::RefCell是单线程场景下的内部可变性容器,仅在运行时检查借用规则,没有线程同步的开销,完全适配你的场景:

use tokio::join;
use std::cell::RefCell;

struct Time {
  hour: u8,
  minute: u8
}

impl Time {
  pub fn set_time(&mut self, hour: u8, minute: u8) {
      self.hour = hour;
      self.minute = minute;
  }
}

fn main() {
  let mut runtime_builder = tokio::runtime::Builder::new_current_thread();
  runtime_builder.enable_time();
  let runtime = runtime_builder.build().unwrap();
  runtime.block_on(async_main());
}

async fn async_main() {
  // 用RefCell包裹Time,获得内部可变性
  let time = RefCell::new(Time {hour: 0, minute: 0});

  join!(
    task_1(&time),
    task_2(&time)
  );
}

async fn task_1(time: &RefCell<Time>) {
  loop {
    // 临时获取可变借用,调用结束后借用立刻释放,不会跨await点
    time.borrow_mut().set_time(10, 20);
    tokio::task::yield_now().await;
  }
}

async fn task_2(time: &RefCell<Time>) {
  loop {
    time.borrow_mut().set_time(12, 30);
    tokio::task::yield_now().await;
  }
}

注意事项

只要保证RefCell的可变借用不会跨await点持有,就不会出现运行时借用冲突,你的set_time方法是无等待的同步逻辑,完全符合这个要求,不会出现panic。

方案2:消息传递(无运行时检查开销)

如果不想依赖运行时借用检查,可以通过通道将修改请求发送给持有Time所有权的父任务,由父任务统一修改状态,完全规避共享可变状态:

  • 定义消息枚举表示修改请求
  • 两个子任务通过发送端发送修改请求
  • 父任务接收请求并更新Time状态
    这种方式完全在编译期保证安全,适合逻辑更复杂的单线程异步场景。

内容的提问来源于stack exchange,提问作者Josu Goñi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 10:00:03