Threads

Создание

Потоки - объект ОС, способ разделения работы ПО. На обработку потоков могут назначаться разные ядра, а могут и не назначаться. Потоки выгодно использовать тогда, когда они дают выигрыш во времени больше, чем время на их создание (на x86 процессорах = ~9000 наносек, на ARM процессорах = ~27000 наносек). Обычно, это интенсивные по вычислениям приложения. Для интенсивным по вводу-выводу приложений следует использовать async/await вместо потоков.

Пример создания:

fn hello_thread() {
    println!("Hello from thread")
}

fn main() {
    println!("Hello from the MAIN thread");

    let thread_handle = std::thread::spawn(hello_thread);
    thread_handle.join().unwrap(); // нужно соединить новый поток с главным
    // потоком программы, иначе он может не успеть вернуть данные до
    // завершения главного потока программы
}

Потоки являются владельцами данных, поэтому нужно передавать им данные перемещением, чтобы они были живы к моменту запуска потока:

fn do_math(i: u32) -> u32 {
    let mut n = i + 1;
    for _ in 0..10 {
        n *= 2;
    }
    n
}

fn main() {
    println!("Hello from the MAIN thread");

    let mut thread_handles = Vec::new(); // вектор указателей потоков
    for i in 0..10 {
        let thread_handle = std::thread::spawn(move || do_math(i));
        thread_handles.push(thread_handle); // добавить поток к вектор
    }
    for h in thread_handles.into_iter() {
        println!("{}", h.join().unwrap()); // соединение потоков с главным.
    }                                // и вывод результата каждого потока. 

Разделение задачи на потоки

Простой вариант - поделить вектор со значениями на куски (chunks), и под обработку каждого куска сделать отдельный поток, после чего собрать потоки вместе:

fn main() {
    const N_THREADS: usize = 8;
    let to_add = (0..5000).collect::<Vec<u32>>(); // вектор от 0 до 4999
    let mut thread_handles = Vec::new(); // вектор указателей потоков
    let chunks = to_add.chunks(N_THREADS); // размер кусков разбиения

    for chunk in chunks {
        let my_chunk = chunk.to_owned(); // обход borrow checker/lifetime
        thread_handles.push(std::thread::spawn(move || my_chunk.iter().sum::<u32>())); // создание потоков с принадлежащими им данными
    }

    // суммирование потоков-кусков в одно число
    let mut sum: u32 = 0;
    for handle in thread_handles {
        sum += handle.join().unwrap()
    }
    println!("Sum is {sum}");
}