Каков правильный способ атомарного обновления вектора или фрагмента, который перекрывается между несколькими потоками? - PullRequest
0 голосов
/ 22 ноября 2018

Я хочу поработать над вектором, совместно используемым несколькими потоками, но я не хочу использовать Mutex, потому что он не без ожидания.

Код ниже написан так, как я бы вC.

#![feature(core_intrinsics, ptr_internals)]

use std::intrinsics::atomic_xadd_rel;
use std::ptr::Unique;
use std::thread::spawn;

fn main() {
    let mut data = [0; 8];
    let mut pool = Vec::with_capacity(8);
    for index in 0..8 {
        let data_ptr = Unique::new(data.as_mut_ptr());
        pool.push(spawn(move || {
            println!("Thread {} -> {}", index, unsafe {
                atomic_xadd_rel(
                    data_ptr
                        .unwrap()
                        .as_ptr()
                        .add(if index % 2 != 0 { index - 1 } else { index }),
                    1,
                )
            });
        }));
    }
    for work in pool {
        work.join().unwrap();
    }
    println!("Data {:?}", data);
}

Я также написал код, используя только стабильный API:

use std::iter::repeat_with;
use std::sync::atomic::{AtomicUsize, Ordering::*};
use std::sync::Arc;
use std::thread::spawn;

fn main() {
    let data = Arc::new(
        repeat_with(|| AtomicUsize::new(0))
            .take(8)
            .collect::<Vec<_>>(),
    );
    let mut pool = Vec::with_capacity(8);
    for index in 0..8 {
        let data_clone = data.clone();
        pool.push(spawn(move || {
            let offset = index - (index % 2 != 0) as usize;
            println!(
                "Thread {} -> {}",
                index,
                data_clone[offset].fetch_add(1, Relaxed)
            );
        }));
    }
    for work in pool {
        work.join().unwrap();
    }
    println!("Data {:?}", data);
}

Этот код возвращает

Thread 0 -> 0
Thread 1 -> 1
Thread 3 -> 0
Thread 5 -> 1
Thread 7 -> 1
Thread 2 -> 1
Thread 6 -> 0
Thread 4 -> 0
Data [2, 0, 2, 0, 2, 0, 2, 0]

Есть ли правильныйспособ сделать это в Rust?

Я не думаю, что это дубликат Как передать непересекающиеся фрагменты из вектора в разные потоки? , потому что мои элементы вектора / слайса перекрываются между потоками,В моем примере каждый нечетный индекс среза увеличивается вдвое двумя разными потоками.

1 Ответ

0 голосов
/ 22 ноября 2018

Предполагая, что каждый поток имеет уникальный доступ к определенному элементу или подрезку вашего вектора, это может быть случай использования split_at (или одной из аналогичных функций).split_at разбивает изменяемый фрагмент на два независимых изменяемых фрагмента;Вы можете вызывать его несколько раз, чтобы разделить свой срез на правильное количество сегментов, и передать каждый подрезок в отдельный поток.

Лучший способ передать подрезы в поток - использоватьчто-то вроде областей видимости в перекладина .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...