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

如何在Rust中利用多线程并发修改Arc包裹数组的不同元素?

如何在Rust中利用多线程并发修改Arc包裹数组的不同元素?

首先得帮你找出原代码里的两个核心问题:

  1. Arc::get_mut的使用误区:Arc::get_mut只有当当前Arc是数据的唯一所有者时才会返回Some,你在循环里多次clone了Arc,每个线程都持有一个Arc实例,此时数据有多个所有者,get_mut自然返回None,unwrap就触发panic了。
  2. 数组索引越界:你的循环范围是1..=2,但数组[[0;2];2]的有效索引是0和1,i=2会直接触发越界错误,这也是个隐藏的风险点。

下面给你三种可行的解决方案,你可以根据自己的需求选择:

方案一:用Arc<UnsafeCell>结合unsafe(高性能,需保证线程安全)

这种方式适合追求极致性能的场景,因为我们明确每个线程只修改互不重叠的行,所以unsafe操作是安全的:

#![allow(dead_code)]
use std::sync::Arc;
use std::cell::UnsafeCell;
use std::thread;
use std::time::Instant;

fn main() {
    // 用UnsafeCell包裹数组,允许内部可变访问
    let arr = Arc::new(UnsafeCell::new([[0; 2]; 2]));
    let t1 = Instant::now();
    let mut handles = Vec::new();

    // 修正索引范围为0..2
    for i in 0..2 {
        let arr_clone = Arc::clone(&arr);
        let handle = thread::spawn(move || {
            // 获取原始指针,因为我们保证线程只修改独立行,所以unsafe是安全的
            let ptr = arr_clone.get();
            unsafe {
                (*ptr)[i] = [10, 100];
            }
        });
        handles.push(handle);
    }

    // 等待所有线程完成
    for h in handles {
        h.join().unwrap();
    }

    let tot1 = t1.elapsed();
    // 安全读取最终结果(所有线程已完成,无数据竞争)
    let final_arr = unsafe { &*arr.get() };
    println!("arr = {:?}", final_arr);
    println!("time consumed: {:?}", tot1);
}

方案二:用Mutex包裹每行(完全安全,无unsafe)

这种方式不需要写unsafe代码,每个行单独用Mutex保护,因为修改的是不同行,几乎不会有锁竞争,性能损失可以忽略:

#![allow(dead_code)]
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Instant;

fn main() {
    // 数组的每一行都用Mutex包裹
    let arr = Arc::new([Mutex::new([0; 2]), Mutex::new([0; 2])]);
    let t1 = Instant::now();
    let mut handles = Vec::new();

    for i in 0..2 {
        let arr_clone = Arc::clone(&arr);
        let handle = thread::spawn(move || {
            // 获取当前行的锁并修改
            let mut row = arr_clone[i].lock().unwrap();
            *row = [10, 100];
        });
        handles.push(handle);
    }

    for h in handles {
        h.join().unwrap();
    }

    let tot1 = t1.elapsed();
    // 收集每一行的结果
    let final_arr: [[i32;2];2] = [
        *arr[0].lock().unwrap(),
        *arr[1].lock().unwrap(),
    ];
    println!("arr = {:?}", final_arr);
    println!("time consumed: {:?}", tot1);
}

方案三:使用作用域线程(最简洁,无需Arc)

如果你的线程不需要脱离当前函数的作用域,推荐用crossbeam库的作用域线程,代码更简洁,还能避免Arc的克隆开销:
首先需要在Cargo.toml中添加依赖:

[dependencies]
crossbeam = "0.8"

然后编写代码:

#![allow(dead_code)]
use crossbeam::thread;
use std::time::Instant;

fn main() {
    let mut arr = [[0; 2]; 2];
    let t1 = Instant::now();

    // 作用域线程可以直接借用数组的可变引用,无需Arc
    thread::scope(|s| {
        for i in 0..2 {
            s.spawn(move |_| {
                arr[i] = [10, 100];
            });
        }
    }).unwrap();

    let tot1 = t1.elapsed();
    println!("arr = {:?}", arr);
    println!("time consumed: {:?}", tot1);
}

方案选择建议

  • 追求极致性能且能保证线程访问安全:选方案一;
  • 不想写unsafe代码,优先安全:选方案二;
  • 线程不脱离当前函数作用域:选方案三,代码最简洁高效。

备注:内容来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 08:03:00