如何在Rust中的线程之间共享不可变数据?

卡尔文·戈弗雷(Calvin Godfrey)

我正在Rust中开发一种光线追踪器,作为一种学习语言的方法,单线程版本可以正常工作。我想通过多线程来加快速度,而在C / C ++中对光线跟踪器进行多线程相对容易,因为共享的大多数数据都是只读的(唯一的问题是在写入像素数据时发生的)。但是我在Rust及其所有权规则上遇到了很多麻烦。

我有一个特点Hittable: Send + Sync针对不同类型的东西(球,网格),可以得到击中在世界上,和我离开的实施SendSync空白,因为我实际上并不需要任何人。然后我有一个VEC类型的世界对象Vec<Box<dyn Hittable>>对于实际的多线程,我正在尝试如下操作:

    let pixels_mutex: Arc<Mutex<Vec<Vec<(f64, f64, f64, u32)>>>> = Arc::new(Mutex::new(pixels));
    let vec_arc: Arc<Vec<Box<dyn Hittable>>> = Arc::new(vec);

    let mut thread_vec: Vec<thread::JoinHandle<()>> = Vec::new();

    for _ in 0..NUM_THREADS {
        let camera_clone = camera.clone();
        thread_vec.push(thread::spawn(move || {
            for r in 0..RAYS_PER_THREAD {
                if r % THREAD_UPDATE == 0 {
                    println!("Thread drawing ray {} of {} ({:.2}%)", r, RAYS_PER_THREAD, (r as f64 * 100.) / (RAYS_PER_THREAD as f64));
                }
                let u: f64 = util::rand();
                let v: f64 = util::rand();
                let ray = camera_clone.get_ray(u, v);
                let res = geometry::thread_safe_cast_ray(&ray, &vec_arc, MAX_DEPTH);
                let i = (u * IMAGE_WIDTH as f64).floor() as usize;
                let j = (v * IMAGE_HEIGHT as f64).floor() as usize;
                util::thread_safe_increment_color(&pixels_mutex, j, i, &res);
            }
        }));
    }

    for handle in thread_vec {
        handle.join().unwrap();
    }

我已经thread_safe_increment_color实现了,这看起来还不错,但是我一直thread_safe_cast_ray等到这个循环开始工作为止。我在这段代码中遇到的问题是,每个线程都试图vec_arc进入其闭包,这违反了所有权规则。我曾尝试制作与vec_arc一样的克隆camera,但编译器不允许我这样做,我认为这是因为我的Hittable特质不需要CopyorClone特质。我实现的结构Hittable不能仅仅derive(Copy, Clone)因为它们包含一个Box<dyn Material>,而其中Material的另一个特征代表了对象的材质。

我认为这样做会容易得多,因为我知道大多数数据(除外pixels_mutex)都是只读的。我如何在正在创建的线程之间共享vec_arc(就此而言pixels_mutex)?

用户名

您应该vec_arc像使用相机一样进行克隆由于它是Arc,因此克隆很便宜,并且不需要Vec和其元素都是可克隆的您只需要确保克隆智能指针而不是向量,并使用关联的函数即可:

let vec_arc = Arc::clone(&vec_arc);

另一种选择是使用作用域线程,在这种情况下,您根本不需要使用线程Arc您只需将闭包设置为非闭包,move然后直接访问局部变量即可。您的代码将如下所示:

crossbeam::scope(|s| {
    for _ in 0..NUM_THREADS {
        let camera_clone = camera.clone();
        thread_vec.push(s.spawn(|_| {
            // your code here, using `vec` directly instead of `vec_arc`
            ...
        }).unwrap());
    }
}).unwrap();  // here threads are joined automatically

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章