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

Rust:使用Arc与Mutex多线程修改变量遇E0507错误求助

多线程修改共享变量的所有权错误及解决

问题描述

使用Arc和Mutex实现多线程修改同一个变量时,克隆Arc后仍触发错误:error[E0507]: cannot move out of dereference of "MutexGuard<_, TestStruct>",报错位置均为test.lock().unwrap()调用处。需求是通过多线程对计数器进行递增操作,最终查看计数器值。曾尝试将变量放入Option(Arc::new(Mutex::new(Some(TestStruct{..})))),通过take()取出值,但取出后Option会被替换为None,仅能生效一次。

附原代码:

// modifed example from https://doc.rust-lang.org/beta/rust-by-example/std/arc.html
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

fn main() {
    // This variable declaration is where its value is specified.
    let test = Arc::new(Mutex::new(TestStruct{
        counter: 0,
    }));
    
    println!("Initial Counter Value: 0");

    for _ in 0..10 {
        // Here there is no value specification as it is a pointer to a
        // reference in the memory heap.
        let test = Arc::clone(&test);

        thread::spawn(move || {
            // As Arc was used, threads can be spawned using the value allocated
            // in the Arc variable pointer's location.
            test.lock().unwrap().increment();
        });
    }

    // Make sure all Arc instances are printed from spawned threads.
    thread::sleep(Duration::from_secs(1));
    println!("Final Counter Value: {:?}", test.lock().unwrap().counter);
}

#[derive(Debug)]
struct TestStruct {
    // counter
    counter: i32,
}

impl TestStruct {
    // increment counter
    fn increment(mut self) {
        self.counter +=1;
    }
}

错误原因

核心问题出在increment方法的参数定义:

fn increment(mut self) {
    self.counter +=1;
}

该方法接收的是**self的所有权**,调用时会尝试将TestStruct实例从MutexGuard中移走,但MutexGuard仅允许对内部值进行引用访问,不允许转移所有权,因此触发了所有权转移错误。

而使用Option+take()的方案,本质是一次性取出内部值并置空Option,自然无法支持多线程重复修改的需求。

解决方法

将increment方法改为接收可变引用,这样既可以修改内部计数器,又不会转移所有权:

修改后的完整代码

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let test = Arc::new(Mutex::new(TestStruct{
        counter: 0,
    }));
    
    println!("Initial Counter Value: 0");

    // 收集线程句柄,确保所有线程执行完成
    let mut handles = Vec::new();
    for _ in 0..10 {
        let test = Arc::clone(&test);

        let handle = thread::spawn(move || {
            let mut guard = test.lock().unwrap();
            guard.increment();
        });
        handles.push(handle);
    }

    // 等待所有线程执行完毕
    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final Counter Value: {:?}", test.lock().unwrap().counter);
}

#[derive(Debug)]
struct TestStruct {
    counter: i32,
}

impl TestStruct {
    // 接收可变引用,不转移所有权
    fn increment(&mut self) {
        self.counter +=1;
    }
}

额外优化说明

原代码使用thread::sleep等待线程结束的方式并不严谨,可能因线程执行耗时超过等待时间导致结果不准确。通过收集JoinHandle并调用join(),可以确保所有线程执行完成后再打印最终结果,保证数据的正确性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 12:20:35