Запуск потоков
Выполняйте код параллельно.
«Запуск потоков» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.
Что такое поток?
Поток позволяет программе выполнять код параллельно. Операционная система может планировать несколько потоков, поэтому их работа может выполняться одновременно.
Стандартная библиотека Rust предоставляет потоки через модуль std::thread. Это собственные потоки ОС, иногда называемые потоками 1:1.
В этом уроке Вы научитесь запускать новые потоки и управлять их выполнением.
Запуск с помощью thread::spawn
Новый поток создаётся вызовом thread::spawn с передачей ему замыкания. Замыкание содержит код, который будет выполнять новый поток.
Вызов немедленно возвращает JoinHandle, а запущенный поток продолжает работать в фоновом режиме.
use std::thread;
fn main() {
thread::spawn(|| {
println!("hello from a thread");
});
println!("hello from main");
}main может завершиться первым
Когда main возвращает управление, весь процесс завершается, даже если запущенные потоки ещё работают.
Поэтому приведённая выше программа может вывести только сообщение из main. Фоновый поток может не успеть запуститься до завершения процесса.
Нам нужен способ дождаться завершения потоков.
Ожидание с помощью join
У возвращённого spawn объекта JoinHandle есть метод join. Его вызов блокирует текущий поток, пока запущенный поток не завершится.
Это гарантирует, что запущенный поток полностью выполнится до продолжения работы main.
use std::thread;
fn main() {
let handle = thread::spawn(|| {
println!("worker done");
});
handle.join().unwrap();
println!("main done");
}Перемешивание вывода
Когда два потока работают одновременно, их вывод может перемешиваться непредсказуемым образом. Порядок выполнения выбирает ОС.
Два запуска одной программы могут дать разный порядок. Без синхронизации никогда не полагайтесь на определённый порядок.
use std::thread;
fn main() {
let handle = thread::spawn(|| {
for i in 1..4 {
println!("thread: {}", i);
}
});
for i in 1..4 {
println!("main: {}", i);
}
handle.join().unwrap();
}Приостановка с помощью sleep
Поток можно приостановить с помощью thread::sleep, принимающего Duration. Это освобождает CPU, чтобы другие потоки могли продолжить работу.
Приостановка полезна в примерах, чтобы сделать перемешивание вывода заметнее, но не используйте её для настоящей синхронизации.
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..4 {
println!("spawned: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
thread::sleep(Duration::from_millis(10));
}Запуск множества потоков
Можно запустить несколько потоков в цикле и собрать их дескрипторы в вектор.
Затем переберите вектор и вызовите join для каждого дескриптора, чтобы каждый поток завершился до окончания программы.
use std::thread;
fn main() {
let mut handles = vec![];
for id in 0..3 {
let h = thread::spawn(move || {
println!("thread {}", id);
});
handles.push(h);
}
for h in handles {
h.join().unwrap();
}
}Именование потоков с помощью Builder
Тип thread::Builder позволяет настроить поток перед запуском. Можно задать имя и размер стека.
Именованные потоки упрощают чтение сообщений о панике и отладку.
use std::thread;
fn main() {
let h = thread::Builder::new()
.name("worker".into())
.spawn(|| {
println!("running in named thread");
})
.unwrap();
h.join().unwrap();
}Паники остаются в потоке
Если в запущенном потоке происходит паника, по умолчанию она не завершает всю программу. Паника остаётся в этом потоке.
При вызове join для потока, завершившегося паникой, Вы получаете Err. Это позволяет родительскому потоку обнаружить сбой.
use std::thread;
fn main() {
let h = thread::spawn(|| {
panic!("boom");
});
let result = h.join();
println!("joined, is_err = {}", result.is_err());
}Информация о текущем потоке
Работающий поток можно исследовать с помощью thread::current. Он возвращает дескриптор, чей метод name выдаёт необязательное имя потока.
Главный поток также является обычным потоком и обычно называется main.
use std::thread;
fn main() {
let current = thread::current();
println!("name: {:?}", current.name());
}Возврат значений из потоков
Замыкание, переданное в spawn, может возвращать значение. При вызове join это значение возвращается внутри Ok.
Так удобно вычислить что-либо в другом потоке, а затем получить результат.
use std::thread;
fn main() {
let h = thread::spawn(|| {
let sum: i32 = (1..=10).sum();
sum
});
let total = h.join().unwrap();
println!("total = {}", total);
}Быстрая проверка
Проверьте, насколько хорошо Вы понимаете запуск потоков.
Итоги
Вы научились запускать потоки с помощью thread::spawn и замыкания, которое возвращает JoinHandle.
Вызов join ожидает завершения и возвращает значение или сообщает о панике. Вывод разных потоков может непредсказуемо перемешиваться, а Builder позволяет задавать потокам имена.
Далее Вы увидите, как перемещать данные в запускаемые потоки.
Часто задаваемые вопросы
Урок «Запуск потоков» бесплатный?
Да — полный текст урока «Запуск потоков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.
Чему я научусь в уроке «Запуск потоков»?
Выполняйте код параллельно. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Запуск потоков»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Запуск потоков
- Передача данных в потоки
- Совместный доступ с Arc и Mutex
- Объединение потоков и сбор результатов