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

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

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

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

Маркерный трейт Send указывает, что владение значениями типа, реализующего Send, можно передавать между потоками. Почти каждый тип Rust реализует Send, но есть некоторые исключения, включая 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 указывает, что на тип, реализующий 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>, которые безопасно использовать в конкурентных контекстах. Система типов и проверщик заимствований гарантируют, что код, использующий эти решения, не приведет к гонкам данных или недопустимым ссылкам. После того как ваш код скомпилируется, вы можете быть уверены, что он спокойно будет выполняться в нескольких потоках без трудноотслеживаемых ошибок, распространенных в других языках. Конкурентное программирование больше не является концепцией, которой нужно бояться: идите и делайте ваши программы конкурентными, бесстрашно!