Многопоточность в Rust: как Ownership, Send и Sync помогают бороться с гонками данных

NikiZip 14 минут назад Краткое содержание статьи Многопоточность в Rust: как Ownership, Send и Sync помогают бороться с гонками данных Средний 7 мин 239 Rust * Программирование * Системное программирование * Туториал...
Will Anthropic have the best AI model at the end of October 2026?
В сфере искусственного интеллекта произошло заметное событие. NikiZip 14 минут назад Краткое содержание статьи Многопоточность в Rust: как Ownership, Send и Sync помогают бороться с гонками данных Средний 7 мин 239 Rust * Программирование * Системное программирование * Туториал Всем привет! В предыдущей статье мы с вами разобрали классическую проблему C++: то есть десять потоков увеличивают общий счётчик, но из-за гонки данных программа может работать неправильно. В Rust мы с вами попробуем решить ту же задачу.
Главное отличие заключается не в том, что Rust умеет создавать потоки каким-то особенным способом, а в модели владения и системе типов: компилятор не позволяет безопасному коду произвольно разделять изменяемые данные между потоками. Но это не значит, что Rust автоматически исправляет любую ошибку многопоточности. Логические ошибки, взаимные блокировки и неправильная синхронизация по-прежнему возможны.
Технические детали
Разберёмся на практических примерах. Все примеры рассчитаны на стабильный Rust и edition 2021. Создаём несколько потоковПрограмма запускает десять потоков.
Каждый поток считает собственный результат, а главный поток складывает значения. use std::thread; fn main() { const THREAD_COUNT: usize = 10; const INCREMENTS: usize = 1_000_000; let mut handles = Vec::new(); for _ in 0.. THREAD_COUNT { let handle = thread::spawn(move { let mut counter = 0; for _ in 0.
push(handle); } let total: usize = handles . expect("Поток завершился с паникой . ("Counter: {total} }Результат:Counter: 10000000Каждый поток владеет собственным счётчиком.
Отраслевые последствия
Поэтому синхронизировать его изменения не требуется. Посмотрите на move: Здесь move позволяет замыканию владеть захваченными значениями, если такие значения есть. Сам counter при этом вообще не передаётся из главного потока — он создаётся отдельно внутри каждого потока.
thread::spawn создаёт новый поток, а join() позволяет дождаться его завершения и получить результат. Но если дочерний поток завершился с паникой, join() вернёт ошибку, которую в примере мы обрабатываем через expect. Это как раз простой пример, но он уже показывает важную идею: не всегда нужно синхронизировать общий ресурс — иногда лучше вообще не создавать его.
Почему нельзя просто разделить изменяемый счётчик? Попробуем сделать задачу иначе:use std::thread; fn main() { let mut counter = 0; let mut handles = Vec::new(); for _ in 0.. push(thread::spawn( { counter += 1; })); } for handle in }Такой код не будет компилироваться.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.




