Материал: Язык программирования Rust

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Файл: src/lib.rs
Листинг 20-24: Отправка
Message::Terminate
рабочим потокам перед вызовом
join
в каждом потоке
Теперь мы дважды проходим по потокам "работников": один раз для отправки одного сообщения
Terminate каждому потоку и один раз для вызова join у каждого рабочего потока. Если бы мы попытались отправить сообщение и сразу же выполнить join в этом же цикле, мы не смогли бы гарантировать, что рабочий поток в текущей итерации получит сообщение из канала.
Чтобы лучше понять, почему нужны два отдельных цикла, представьте сценарий с двумя работниками. Если бы мы использовали один цикл для перебора каждого работника, на первой итерации сообщение о прекращении работы было бы отправлено в канал и метод join вызывался бы у первого рабочего потока. Если этот первый поток занят обработкой запроса в данный момент, второй рабочий поток получит сообщение о завершении из канала и завершит свою работу. В главном потоке мы бы остались в ожидании завершения работы первого рабочего потока, но этого не произошло, потому что второй поток получил сообщение о завершении. Это ситуация взаимной блокировки
(deadlock)!
Чтобы предотвратить такой сценарий, мы сначала помещаем все сообщения
Terminate в канал в первом цикле; затем мы объединяем (join) для завершения все потоки во втором цикле. Каждый рабочий поток прекратит получать запросы из канала, как только получит сообщение о завершении. Таким образом, мы можем быть уверены, что если мы отправим количество завершающих сообщений равное количеству рабочих потоков то,
каждый получит сообщение о завершении до вызова join в его потоке.
impl
Worker { fn new
(id: usize
, receiver: Arc>>) -> Worker { let thread = thread::spawn(
move
|| loop
{ match receiver.lock().unwrap().recv() {
Ok
(job) => { println!
(
"Worker {id} got a job; executing."
); job();
}
Err
(_) => { println!
(
"Worker {id} disconnected; shutting down."
); break
;
}
}
});
Worker { id, thread:
Some
(thread),
}
}
}

Чтобы увидеть этот код в действии, давайте изменим main
, чтобы принимать только два запроса, прежде чем корректно завершить работу сервера как показано в листинге 20-
25.
Файл: src/bin/main.rs
Код 20-25. Выключение сервера после обслуживания двух запросов с помощью выхода из цикла
Вы бы не хотели, чтобы реальный веб-сервер отключался после обслуживания только двух запросов. Этот код всего лишь демонстрирует, что корректное завершение работы и освобождение ресурсов находятся в рабочем состоянии.
Метод take определён в типаже
Iterator и ограничивает итерацию максимум первыми двумя элементами.
ThreadPool выйдет из области видимости в конце main и будет запущена его реализация drop
Запустите сервер с cargo run и сделайте три запроса. Третий запрос должен выдать ошибку и в терминале вы должны увидеть вывод, подобный следующему:
Вы возможно увидите другой порядок рабочих потоков и напечатанных сообщений. Мы можем увидеть, как этот код работает по сообщениям: "работники" номер 0 и 3 получили первые два запроса, затем на третьем запросе сервер прекратил принимать соединения.
Когда
ThreadPool выходит из области видимости в конце main
, то срабатывает его реализация типажа
Drop и пул сообщает всем рабочим потокам прекратить выполнение. Каждый рабочий поток распечатывает сообщение, когда видит сообщение о завершении, а затем пул потоков вызывает join
, чтобы завершить работу каждого рабочего потока.
{{#rustdoc_include ../listings/ch20-web-server/listing-
20
-
25
/src/bin/main.rs:here}}
$
cargo run
Compiling hello v0.1.0 (file:///projects/hello)
Finished dev [unoptimized + debuginfo] target(s) in 1.0s
Running `target/debug/main`
Worker 0 got a job; executing.
Worker 3 got a job; executing.
Shutting down.
Sending terminate message to all workers.
Shutting down all workers.
Shutting down worker 0
Worker 1 was told to terminate.
Worker 2 was told to terminate.
Worker 0 was told to terminate.
Worker 3 was told to terminate.
Shutting down worker 1
Shutting down worker 2
Shutting down worker 3

Обратите внимание на один интересный аспект этого конкретного выполнения:
ThreadPool отправил сообщения о завершении в канал и прежде чем какой-либо рабочий получил сообщение, мы пытались присоединить (join) "работника" с номером 0.
Рабочий поток 0 ещё не получил сообщение о прекращении, поэтому основной поток заблокировал ожидание потока 0 для завершения. Тем временем каждый из рабочих потоков получил сообщения об завершении. Когда рабочий поток 0 завершился,
основной поток ждал окончания завершения выполнения остальных рабочих потоков. В
этот момент все они получили сообщение о завершении и смогли завершиться.
Поздравления! Теперь мы завершили проект; у нас есть базовый веб-сервер, который использует пул потоков для асинхронных ответов. Мы можем выполнить корректное завершение работы сервера, который очищает все потоки в пуле.
Вот полный код для справки:
Файл: src/bin/main.rs
Файл: src/lib.rs
В коде можно сделать больше! Если вы хотите продолжить совершенствование этого проекта, вот несколько идей:
Добавьте больше документации в
ThreadPool и его публичным методам.
Добавьте тесты функционалу из библиотеки.
Заменить вызовы unwrap на более правильную обработку ошибок.
Используйте
ThreadPool для выполнения некоторых других задач, помимо обслуживания веб-запросов.
Найдите крейт для пула потоков на crates.io и реализуйте аналогичный веб-сервер,
используя такой крейт. Затем сравните его API и надёжность с пулом потоков,
который мы реализовали.
Итоги
Отличная работа! Вы сделали это к концу книги! Мы хотим поблагодарить вас за то, что присоединились к нам в этом путешествии по языку Rust. Теперь вы готовы реализовать свои собственные проекты на Rust и помочь с проектами другим людям. Имейте в виду,
что существует приветливое сообщество других Rust разработчиков, которые хотели бы помочь вам с любыми сложными задачами с которыми вы столкнётесь в своём Rust путешествии.
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
08
- final
- code/src/bin/main.rs}}
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
08
- final
- code/src/lib.rs}}

Дополнительная информация
Следующие разделы содержат справочные материалы, которые могут оказаться полезными в вашем путешествии по Rust.

Приложение A: Ключевые слова
Следующий список содержит ключевые слова, зарезервированные для текущего или будущего использования в языке Rust. Как таковые их нельзя использовать в качестве идентификаторов (за исключением сырых идентификаторов, которые мы обсудим в разделе
«Сырые идентификаторы»
). Идентификаторы — это имена функций,
переменных, параметров, полей структур, модулей, крейтов, констант, макросов,
статических значений, атрибутов, типов, свойств или времён жизни.
Ключевые слова, использующиеся в Rust в настоящее время
Ниже приведён список используемых в настоящее время ключевых слов с описанием их функций.
as
— выполнить примитивное преобразование, уточнить конкретную характеристику, которую содержит объект, или переименовать элемент в выражении use async
— вернуть
Future вместо блокирования текущего потока await
— приостановить выполнение до тех пор, пока не будет готов результат
Future break
— немедленно завершить цикл const
— определить константу или неизменяемый указатель continue
— досрочно перейти к следующей итерации цикла crate
— в пути модуля ссылается на корень пакета dyn
— динамическая отсылка к объекту характеристики else
— ветвь для конструкций потока управления if и if let в случае, если никакая другая ветвь не была исполнена enum
— определить перечисление extern
— подключить внешнюю функцию или переменную false
— логический литерал «ложь»
fn
— определить функцию или тип указателя на функцию for
— перебор элементов итератора, реализация характеристики или указание срока жизни более продолжительного периода if
— ветвление на основе результата условного выражения impl
— реализовать функциональность непосредственно или через характеристику in
— часть синтаксиса определения цикла for let
— объявить переменную loop
— безусловный (бесконечный) цикл match
— сопоставить значение с образцами mod
— определить модуль move
— заставить замыкание принять на себя владение всеми своими захватами
mut
— обозначить изменяемость для ссылок, сырых указателей или привязок к шаблонам pub
— обозначить публичную доступность полей структур, блоков impl или модулей ref
— привязка по ссылке return
— возврат из функции
Self
— псевдоним для типа, который мы определяем или реализуем self
— объект, содержащий этот метод или текущий модуль static
— глобальная переменная или время жизни на протяжении всего выполнения программы struct
— определить структуру super
— родительский модуль текущего модуля trait
— определить характеристику true
— логический литерал «истина»
type
— определить псевдоним типа или ассоциированный тип union
— определение union
; является ключевым словом только при использовании в объявлении союза unsafe
— обозначить небезопасный код, функции, характеристики или реализации use
— ввести объекты в область действия where
— обозначить утверждения, которые ограничивают тип while
— цикл, работающий относительно результата условного выражения
Ключевые слова, зарезервированные для будущего использования
Следующие ключевые слова ещё не имеют никакой функциональности, но зарезервированы Rust для возможного использования в будущем.
abstract become box do final macro override priv try typeof unsized virtual yield
Сырые идентификаторы

Сырые идентификаторы — это синтаксис, позволяющий использовать ключевые слова там, где обычно они не могут быть. Для создания и использования сырого идентификатора к ключевому слову добавляется префикс r#
Например, ключевое слово match
. Если вы попытаетесь скомпилировать следующую функцию, использующую в качестве имени match
:
Файл: src/main.rs вы получите ошибку:
Ошибка говорит о том, что вы не можете использовать ключевое слово match в качестве идентификатора функции. Чтобы получить возможность использования слова match в
качестве имени функции, нужно использовать синтаксис «сырых идентификаторов»,
например так:
Файл: src/main.rs
Этот код скомпилируется без ошибок. Обратите внимание, что префикс r#
в определении имени функции указан так же, как он указан в месте её вызова в main
Сырые идентификаторы позволяют вам использовать любое слово, которое вы выберете, в качестве идентификатора, даже если это слово окажется зарезервированным ключевым словом. Это даёт нам больше свободы в выборе имён идентификаторов, а также позволяет нам интегрироваться с программами, написанными на языке, где эти слова не являются ключевыми. Кроме того, необработанные идентификаторы позволяют вам использовать библиотеки, написанные в версии Rust, отличной от используемой в вашем крейте. Например, try не является ключевым словом в выпуске 2015 года, но есть в выпуске 2018 года. Если вы зависите от библиотеки, написанной с использованием версии 2015 года и имеющей функцию try
, вам потребуется использовать синтаксис сырого идентификатора, в данном случае r#try
, для вызова этой функции из кода fn match
(needle: &
str
, haystack: &
str
) -> bool
{ haystack.contains(needle)
} error: expected identifier, found keyword `match`
--> src/main.rs:4:4
|
4 | fn match(needle: &str, haystack: &str) -> bool {
| ^^^^^ expected identifier, found keyword fn r
#
match
(needle: &
str
, haystack: &
str
) -> bool
{ haystack.contains(needle)
} fn main
() { assert!
(r#
match
(
"foo"
,
"foobar"
));
}
версии 2018 года. См.
Приложение E
для получения дополнительной информации о редакциях Rust.

Операторы_и_обозначения'>Дополнение Б: Операторы и обозначения
Это дополнение содержит глоссарий синтаксиса Rust, включая операторы и другие обозначения, которые появляются сами по себе или в контексте путей, обобщений,
типажей, макросов, атрибутов, комментариев, кортежей и скобок.
Операторы
Таблица Б-1 содержит операторы языка Rust, пример появления оператора, короткое объяснение, возможность перегрузки оператора. Если оператор можно перегрузить, то показан типаж, с помощью которого его можно перегрузить.
Таблица Б-1: Операторы
Оператор
Пример
Объяснение
Перегружаемость
!
ident!(...)
, ident!{...}
, ident![...]
Вызов макроса
!
!expr
Побитовое или логическое отрицание
Not
!=
expr != expr
Сравнение "не равно"
PartialEq
%
expr % expr
Остаток от деления
Rem
%=
var %= expr
Остаток от деления и присваивание
RemAssign
&
&expr
,
&mut expr
Заимствование
&
&type
,
&mut type
,
&'a type
,
&'a mut type
Указывает что данный тип заимствуется
&
expr & expr
Побитовое И
BitAnd
&=
var &= expr
Побитовое И и присваивание
BitAndAssign
&&
expr && expr
Логическое И
*
expr * expr
Арифметическое умножение
Mul
*=
var *= expr
Арифметическое умножение и присваивание
MulAssign

Оператор
Пример
Объяснение
Перегружаемость
*
*expr
Разыменование ссылки
Deref
*
*const type
,
*mut type
Указывает, что данный тип является сырым указателем
+
trait + trait
,
'a + trait
Соединение ограничений типа
+
expr + expr
Арифметическое сложение
Add
+=
var += expr
Арифметическое сложение и присваивание
AddAssign
,
expr, expr
Аргумент и разделитель элементов
-
- expr
Арифметическое отрицание
Neg
- expr - expr
Арифметическое вычитание
Sub
- var -= expr
Арифметическое вычитание и присваивание
SubAssign
->
fn(...) -> type
,
|...|
-> type expr.ident
Доступ к элементу
, expr..
,
..expr
, expr..expr
Указывает на диапазон чисел,
исключая правый
PartialOrd
..=
..=expr
, expr..=expr
Указывает на диапазон чисел,
включая правый
PartialOrd
..expr
Синтаксис обновления структуры variant(x, ..)
,
struct_type { x, .. }
Привязка «И все остальное»

1   ...   54   55   56   57   58   59   60   61   62
ОператорПримерОбъяснениеПерегружаемостьexpr...expr(Устарело,используйте новый синтаксис ..=) Используется при определении инклюзивного диапазона/expr / exprАрифметическое делениеDiv/=var /= exprАрифметическое деление и присваиваниеDivAssign:pat: type, ident: typeОграничения типов:ident: exprИнициализация поля структуры:'a: loop {...}Метка цикла;expr;Оператор,указывающий на конец высказывания;[...; len]Часть синтаксиса массива фиксированного размера<<expr << exprБитовый сдвиг влевоShl<<=var <<= exprБитовый сдвиг влево и присваиваниеShlAssign<expr < exprСравнение "меньше чем"PartialOrd<=expr <= exprСравнение "меньше или равно"PartialOrd=var = expr, ident = typeПрисваивание/эквивалентность==expr == exprСравнение "равно"PartialEq=>pat => exprЧасть синтаксиса конструкции match

Оператор
Пример
Объяснение
Перегружаемость
>
expr > expr
Сравнение "больше чем"
PartialOrd
>=
expr >= expr
Сравнение "больше или равно"
PartialOrd
>>
expr >> expr
Битовый сдвиг вправо
Shr
>>=
var >>= expr
Битовый сдвиг вправо и присваивание
ShrAssign
@
ident @ pat
Pattern binding
^
expr ^ expr
Побитовое исключающее ИЛИ
BitXor
^=
var ^= expr
Побитовое исключающее ИЛИ
и присваивание
BitXorAssign
&vert;
pat &vert; pat
Альтернативные шаблоны
&vert;
expr &vert; expr
Побитовое ИЛИ
BitOr
&vert;=
var &vert;= expr
Побитовое ИЛИ и присваивание
BitOrAssign
&vert;&vert;
expr
&vert;&vert; expr
Короткое логическое ИЛИ
?
expr?
Возврат ошибки
Обозначения не-операторы
Следующий список содержит все символы, которые не работают как операторы; то есть они не ведут себя как вызов функции или метода.
Таблица Б-2 показывает символы, которые появляются сами по себе и допустимы в различных местах.
Таблица Б-2: Автономный синтаксис
Обозначение
Объяснение
'ident
Именованное время жизни или метка цикла

Обозначение
Объяснение
...u8
,
...i32
,
...f64
,
...usize
,
etc.
Числовой литерал определённого типа "..."
Строковый литерал r"..."
, r#"..."#
, r##"..."##
, etc.
Необработанный строковый литерал, в котором не обрабатываются escape-символы b"..."
Строковый литерал байтов; создаёт массив байтов вместо строки br"..."
, br#"..."#
, br##"..."##
, etc.
Необработанный строковый байтовый литерал,
комбинация необработанного и байтового литерала '...'
Символьный литерал b'...'
ASCII байтовый литерал
&vert;...&vert; expr
Замыкание
!
Всегда пустой тип для расходящихся функций
_
«Игнорируемое» связывание шаблонов; также используется для читабельности целочисленных литералов
Таблица Б-3 показывает обозначения которые появляются в контексте путей иерархии модулей
Обозначение_Объяснение'>Таблица Б-3. Синтаксис, связанный с путями
Обозначение
Объяснение
ident::ident
Путь к пространству имён
::path
Путь относительно корня крейта (т. е. явный абсолютный путь)
self::path
Путь относительно текущего модуля (т. е. явный относительный путь).
super::path
Путь относительно родительского модуля текущего модуля type::ident
,
::ident
Ассоциированные константы, функции и типы
::...
Ассоциированный элемент для типа, который не может быть назван прямо (например
<&T>::...
,
<[T]>::...
,
etc.)
trait::method(...)
Устранение неоднозначности вызова метода путём именования типажа, который определяет его
Источник: https://files.student-it.ru/previewfile/264058