Структура Barrier
#![allow(unused)] fn main() { pub struct Barrier { /* приватные поля */ } }
Барьер обеспечивает точку синхронизации, в которой несколько потоков будут ждать, пока все они не достигнут этой точки.
Барьеры используются для обеспечения того, что несколько потоков выполняют определенные участки кода одновременно.
Примеры
#![allow(unused)] fn main() { use std::sync::{Arc, Barrier}; use std::thread; let mut handles = Vec::with_capacity(10); let barrier = Arc::new(Barrier::new(10)); for _ in 0..10 { let c = Arc::clone(&barrier); // То же самое, что и clone: let c = barrier.clone(); handles.push(thread::spawn(move|| { println!("до ожидания"); c.wait(); println!("после ожидания"); })); } // Ждем завершения всех потоков for handle in handles { handle.join().unwrap(); } }
Методы
new
#![allow(unused)] fn main() { pub fn new(n: usize) -> Barrier }
Создает новый барьер, который может блокировать заданное количество потоков.
Барьер будет блокировать вызовы wait(), пока n потоков не будут ожидать на нем. Затем все потоки будут разблокированы одновременно.
Примеры
#![allow(unused)] fn main() { use std::sync::Barrier; let barrier = Barrier::new(10); }
wait
#![allow(unused)] fn main() { pub fn wait(&self) -> BarrierWaitResult }
Блокирует текущий поток до тех пор, пока все потоки не встретятся здесь.
Барьер выполняется циклически: после того как все потоки собрались вместе, они могут продолжать работу, и барьер может быть использован снова.
Возвращает BarrierWaitResult, указывающий, был ли этот поток "лидером" при прорыве барьера.
Только один поток получит BarrierWaitResult, у которого is_leader() возвращает true, когда барьер прорывается, и все остальные потоки получат результат, у которого is_leader() возвращает false.
Примеры
#![allow(unused)] fn main() { use std::sync::{Arc, Barrier}; use std::thread; let mut handles = Vec::with_capacity(10); let barrier = Arc::new(Barrier::new(10)); let leader = Arc::new(std::sync::Mutex::new(None)); for i in 0..10 { let c = Arc::clone(&barrier); let leader_ref = Arc::clone(&leader); handles.push(thread::spawn(move|| { let wait_result = c.wait(); if wait_result.is_leader() { *leader_ref.lock().unwrap() = Some(i); } })); } // Ждем завершения всех потоков for handle in handles { handle.join().unwrap(); } // Проверяем, какой поток был лидером let leader_id = leader.lock().unwrap().unwrap(); println!("Поток {} был лидером", leader_id); }
Поведение
Постоянство
Барьеры можно использовать многократно и будут продолжать работать постоянно.
Потокобезопасность
Барьеры являются Sync и могут использоваться совместно между несколькими потоками.
Производительность
Реализация барьера использует мьютекс и условную переменную, что может быть не самым эффективным решением для высокопроизводительных сценариев. Для таких случаев могут существовать специализированные крейты.
Пример практического использования
Синхронизация начала вычислений:
#![allow(unused)] fn main() { use std::sync::{Arc, Barrier}; use std::thread; use std::time::Instant; let data = Arc::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); let barrier = Arc::new(Barrier::new(4)); let mut handles = vec![]; for i in 0..4 { let data = Arc::clone(&data); let barrier = Arc::clone(&barrier); handles.push(thread::spawn(move || { // Ждем, пока все потоки не будут готовы barrier.wait(); let start = Instant::now(); // Каждый поток обрабатывает свою часть данных let chunk_size = data.len() / 4; let start_idx = i * chunk_size; let end_idx = if i == 3 { data.len() } else { start_idx + chunk_size }; let sum: i32 = data[start_idx..end_idx].iter().sum(); (sum, start.elapsed()) })); } let mut total_sum = 0; for handle in handles { let (sum, duration) = handle.join().unwrap(); total_sum += sum; println!("Поток вычислил сумму {} за {:?}", sum, duration); } println!("Общая сумма: {}", total_sum); }
Совместимость
Барьеры доступны с Rust 1.0.0 и являются стандартным способом синхронизации потоков по точкам встречи.