Introdução
Bem-vindo(a) a Concorrência Extensível com as Traits Send e Sync. Este laboratório faz parte do Livro do Rust. Você pode praticar suas habilidades em Rust no LabEx.
Neste laboratório, exploramos os dois conceitos de concorrência em Rust - as traits Send e Sync, que fornecem capacidades de concorrência extensíveis além da biblioteca padrão.
Concorrência Extensível com as Traits Send e Sync
Curiosamente, a linguagem Rust possui muito poucas funcionalidades de concorrência. Quase todas as funcionalidades de concorrência sobre as quais falamos até agora neste capítulo fazem parte da biblioteca padrão, e não da linguagem. Suas opções para lidar com concorrência não se limitam à linguagem ou à biblioteca padrão; você pode escrever suas próprias funcionalidades de concorrência ou usar aquelas escritas por outros.
No entanto, dois conceitos de concorrência estão embutidos na linguagem: as traits std::marker Send e Sync.
Permitindo a Transferência de Propriedade entre Threads com Send
O trait marcador Send indica que a propriedade de valores do tipo que implementa Send pode ser transferida entre threads. Quase todos os tipos Rust são Send, mas existem algumas exceções, incluindo Rc<T>: este não pode ser Send porque se você clonasse um valor Rc<T> e tentasse transferir a propriedade do clone para outra thread, ambas as threads poderiam atualizar a contagem de referência ao mesmo tempo. Por esta razão, Rc<T> é implementado para uso em situações de thread único onde você não quer pagar a penalidade de desempenho thread-safe.
Portanto, o sistema de tipos e as restrições de traits do Rust garantem que você nunca pode, acidentalmente, enviar um valor Rc<T> entre threads de forma insegura. Quando tentamos fazer isso na Listagem 16-14, recebemos o erro the trait Send is not implemented for Rc<Mutex<i32>>. Quando mudamos para Arc<T>, que é Send, o código compilou.
Qualquer tipo composto inteiramente por tipos Send é automaticamente marcado como Send também. Quase todos os tipos primitivos são Send, exceto os ponteiros brutos (raw pointers), que discutiremos no Capítulo 19.
Permitindo Acesso de Múltiplas Threads com Sync
A trait marker Sync indica que é seguro para o tipo que implementa Sync ser referenciado de múltiplas threads. Em outras palavras, qualquer tipo T é Sync se &T (uma referência imutável para T) é Send, o que significa que a referência pode ser enviada com segurança para outra thread. Semelhante a Send, os tipos primitivos são Sync, e os tipos compostos inteiramente por tipos que são Sync também são Sync.
O smart pointer Rc<T> também não é Sync pelas mesmas razões que não é Send. O tipo RefCell<T> (sobre o qual falamos no Capítulo 15) e a família de tipos relacionados Cell<T> não são Sync. A implementação da verificação de empréstimo (borrow checking) que RefCell<T> faz em tempo de execução não é thread-safe. O smart pointer Mutex<T> é Sync e pode ser usado para compartilhar acesso com múltiplas threads, como você viu em "Compartilhando um Mutex<T> Entre Múltiplas Threads".
Implementar Send e Sync Manualmente é Inseguro
Como os tipos que são compostos pelas traits Send e Sync são automaticamente também Send e Sync, não precisamos implementar essas traits manualmente. Como traits marcadores, elas nem sequer têm nenhum método para implementar. Elas são apenas úteis para impor invariantes relacionadas à concorrência.
Implementar manualmente essas traits envolve implementar código Rust inseguro (unsafe). Falaremos sobre o uso de código Rust inseguro no Capítulo 19; por enquanto, a informação importante é que a construção de novos tipos concorrentes que não são compostos por partes Send e Sync requer uma reflexão cuidadosa para manter as garantias de segurança. "The Rustonomicon" em https://doc.rust-lang.org/stable/nomicon tem mais informações sobre essas garantias e como mantê-las.
Resumo
Parabéns! Você concluiu o laboratório Extensible Concurrency With the Send and Sync Traits. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.