Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Расширяемая конкурентность: Send и Sync

Интересно, что почти все средства конкурентности, о которых шла речь в этой главе, — часть стандартной библиотеки, а не языка. Ваши возможности не ограничены языком и стандартной библиотекой: можно писать собственные средства конкурентности или пользоваться написанными другими.

Однако среди ключевых понятий конкурентности есть и встроенные в сам язык, а не в стандартную библиотеку, — это трейты Send и Sync из std::marker.

Передача владения между потоками

Трейт-маркер Send означает, что владение значениями типа, который его реализует, можно передавать между потоками. Send реализуют почти все типы Rust, но есть исключения, в том числе Rc<T>. Он не может реализовать Send: если склонировать значение Rc<T> и передать владение клоном в другой поток, оба потока могут одновременно изменить счётчик ссылок. Поэтому Rc<T> предназначен для однопоточных ситуаций, где не хочется платить производительностью за потокобезопасность.

Таким образом, система типов и ограничения трейтами в Rust гарантируют, что значение Rc<T> нельзя случайно небезопасно отправить в другой поток. Когда мы попытались сделать это в листинге 16-14, то получили ошибку the trait `Send` is not implemented for `Rc<Mutex<i32>>`. А когда перешли на Arc<T>, который Send реализует, код скомпилировался.

Любой тип, целиком состоящий из типов Send, автоматически тоже помечается как Send. Почти все примитивные типы — Send, кроме сырых указателей, о которых мы поговорим в главе 20.

Доступ из нескольких потоков

Трейт-маркер Sync означает, что на значение типа, который его реализует, безопасно ссылаться из нескольких потоков. Иначе говоря, тип T реализует Sync, если &T (неизменяемая ссылка на T) реализует Send — то есть ссылку можно безопасно отправить в другой поток. Как и в случае с Send, все примитивные типы реализуют Sync, а типы, целиком состоящие из типов с Sync, тоже реализуют Sync.

Умный указатель Rc<T> не реализует и Sync — по тем же причинам, по которым не реализует Send. Тип RefCell<T> (о нём мы говорили в главе 15) и родственные типы семейства Cell<T> тоже не реализуют Sync: проверка заимствований, которую RefCell<T> выполняет во время работы, не потокобезопасна. А умный указатель Mutex<T> реализует Sync и годится для общего доступа из нескольких потоков, как вы видели в разделе «Общий доступ к Mutex<T>».

Ручная реализация Send и Sync небезопасна

Раз типы, целиком состоящие из других типов с Send и Sync, реализуют Send и Sync автоматически, реализовывать эти трейты вручную не нужно. Как трейты-маркеры, они даже не содержат методов. Они нужны лишь для того, чтобы обеспечивать инварианты, связанные с конкурентностью.

Ручная реализация этих трейтов требует небезопасного кода на Rust. О небезопасном Rust мы поговорим в главе 20; пока важно понимать, что создание новых конкурентных типов, которые состоят не из частей с Send и Sync, требует тщательного обдумывания, чтобы сохранить гарантии безопасности. Подробнее об этих гарантиях и о том, как их соблюдать, — в «The Rustonomicon».

Итоги

Это не последняя встреча с конкурентностью в книге: следующая глава посвящена асинхронному программированию, а проект из главы 21 применит понятия этой главы в ситуации, более приближенной к реальной, чем небольшие примеры здесь.

Как уже говорилось, в самом языке Rust очень мало средств конкурентности, поэтому многие решения реализованы в виде крейтов. Они развиваются быстрее стандартной библиотеки, так что обязательно поищите в интернете актуальные современные крейты для многопоточных задач.

Стандартная библиотека Rust даёт каналы для передачи сообщений и умные указатели вроде Mutex<T> и Arc<T>, которые безопасно использовать в конкурентном контексте. Система типов и анализатор заимствований гарантируют, что в коде, использующем эти решения, не будет гонок данных и недействительных ссылок. Как только код скомпилировался, можно быть спокойным: он будет прекрасно работать в нескольких потоках — без трудноуловимых багов, обычных для других языков. Конкурентное программирование больше не нужно бояться: вперёд — делайте свои программы конкурентными без страха!