Обычные потоки
Потоки Rust работают аналогично потокам в других языках:
// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 0..10 {
println!("Count in thread: {i}!");
thread::sleep(Duration::from_millis(5));
}
});
for i in 0..5 {
println!("Main thread: {i}");
thread::sleep(Duration::from_millis(5));
}
}
- Создание новых потоков не задерживает автоматически завершение программы в конце
main. - Паники потоков независимы друг от друга.
- Паника может нести полезную нагрузку (payload), которую можно извлечь с помощью
Any::downcast_ref.
- Паника может нести полезную нагрузку (payload), которую можно извлечь с помощью
-
Запустите пример.
- Задержки в 5 мс достаточно велики, чтобы main и порождённые потоки в основном шли в ногу.
- Обратите внимание, что программа завершается до того, как порождённый поток дойдёт до 10!
- Это потому, что
mainзавершает программу, а порождённые потоки её не продлевают.- При желании сравните с
pthreads/std::threadв C++/boost::thread.
- При желании сравните с
-
Как дождаться завершения порождённого потока?
-
thread::spawnвозвращаетJoinHandle. Посмотрите документацию.- У
JoinHandleесть метод.join(), который блокирует выполнение.
- У
-
Используйте
let handle = thread::spawn(...), а затемhandle.join(), чтобы дождаться завершения потока и дать программе досчитать до 10. -
А если мы хотим вернуть значение?
-
Снова посмотрите документацию:
- Замыкание, переданное в
thread::spawn, возвращаетT JoinHandle.join()возвращаетthread::Result<T>
- Замыкание, переданное в
-
Используйте возвращаемое значение
Resultизhandle.join(), чтобы получить доступ к возвращённому значению. -
Хорошо, а как насчёт другого случая?
- Вызовите панику в потоке. Обратите внимание, что это не вызывает панику в
main. - Получите доступ к полезной нагрузке паники. Сейчас хорошее время поговорить о
Any.
- Вызовите панику в потоке. Обратите внимание, что это не вызывает панику в
-
Теперь мы можем возвращать значения из потоков! А как насчёт передачи входных данных?
- Захватите что-нибудь по ссылке в замыкании потока.
- Сообщение об ошибке говорит, что это нужно переместить (move).
- Переместите значение внутрь и убедитесь, что можно вычислить и затем вернуть производное значение.
-
А если мы хотим заимствовать?
mainзавершает дочерние потоки при возврате, но если бы другая функция просто вернула управление, они продолжили бы работать.- Это было бы использование стека после возврата (stack use-after-return), что нарушает безопасность памяти!
- Как этого избежать? См. следующий слайд.