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

Rozszerzalna współbieżność dzięki cechom Send i Sync

Co ciekawe, prawie każda funkcja współbieżności, o której do tej pory mówiliśmy w tym rozdziale, była częścią biblioteki standardowej, a nie języka. Twoje opcje obsługi współbieżności nie ograniczają się do języka ani biblioteki standardowej; możesz pisać własne funkcje współbieżności lub korzystać z tych napisanych przez innych.

Jednakże, wśród kluczowych koncepcji współbieżności, które są osadzone w języku, a nie w bibliotece standardowej, znajdują się cechy znacznika std::marker Send i Sync.

Przenoszenie własności między wątkami

Cecha znacznikowa Send wskazuje, że własność wartości typu implementującego Send może być przenoszona między wątkami. Prawie każdy typ Rust implementuje Send, ale istnieją pewne wyjątki, w tym Rc<T>: Nie może on implementować Send, ponieważ gdybyś sklonował wartość Rc<T> i próbował przenieść własność klonu do innego wątku, oba wątki mogłyby jednocześnie zaktualizować licznik referencji. Z tego powodu Rc<T> jest zaimplementowany do użytku w sytuacjach jednowątkowych, gdzie nie chcesz ponosić kary wydajnościowej związanej z bezpieczeństwem wątków.

Dlatego system typów Rust i ograniczenia cech zapewniają, że nigdy nie możesz przypadkowo wysłać wartości Rc<T> między wątkami w sposób niebezpieczny. Kiedy próbowaliśmy to zrobić w Listingu 16-14, otrzymaliśmy błąd the trait `Send` is not implemented for `Rc<Mutex<i32>>`. Kiedy zmieniliśmy na Arc<T>, który implementuje Send, kod się skompilował.

Każdy typ składający się wyłącznie z typów Send jest automatycznie oznaczany jako Send. Prawie wszystkie typy prymitywne są Send, poza surowymi wskaźnikami, o których będziemy rozmawiać w Rozdziale 20.

Dostęp z wielu wątków

Cecha znacznikowa Sync wskazuje, że bezpieczne jest odwoływanie się do typu implementującego Sync z wielu wątków. Innymi słowy, każdy typ T implementuje Sync, jeśli &T (niemutowalna referencja do T) implementuje Send, co oznacza, że referencja może być bezpiecznie wysłana do innego wątku. Podobnie jak Send, wszystkie typy prymitywne implementują Sync, a typy składające się wyłącznie z typów implementujących Sync również implementują Sync.

Wskaźnik sprytny Rc<T> również nie implementuje Sync z tych samych powodów, dla których nie implementuje Send. Typ RefCell<T> (o którym mówiliśmy w Rozdziale 15) i rodzina powiązanych typów Cell<T> nie implementują Sync. Implementacja sprawdzania pożyczeń, którą RefCell<T> wykonuje w czasie wykonania, nie jest bezpieczna wątkowo. Wskaźnik sprytny Mutex<T> implementuje Sync i może być używany do współdzielenia dostępu z wieloma wątkami, jak widziałeś w sekcji „Współdzielony dostęp do Mutex<T>.

Ręczna implementacja Send i Sync jest niebezpieczna

Ponieważ typy składające się wyłącznie z innych typów, które implementują cechy Send i Sync, automatycznie implementują również Send i Sync, nie musimy implementować tych cech ręcznie. Jako cechy znacznikowe, nie mają one nawet żadnych metod do zaimplementowania. Są po prostu przydatne do egzekwowania niezmienników związanych ze współbieżnością.

Ręczna implementacja tych cech wiąże się z użyciem niebezpiecznego kodu Rust. O używaniu niebezpiecznego kodu Rust będziemy rozmawiać w Rozdziale 20; na razie ważną informacją jest to, że budowanie nowych typów współbieżnych nie składających się z części Send i Sync wymaga starannego przemyślenia w c celu utrzymania gwarancji bezpieczeństwa. „The Rustonomicon” zawiera więcej informacji na temat tych gwarancji i sposobów ich podtrzymywania.

Podsumowanie

To nie jest ostatni raz, kiedy spotkasz się ze współbieżnością w tej książce: następny rozdział skupia się na programowaniu asynchronicznym, a projekt w Rozdziale 21 wykorzysta koncepcje z tego rozdziału w bardziej realistycznej sytuacji niż omówione tutaj mniejsze przykłady.

Jak wspomniano wcześniej, ponieważ niewiele z tego, jak Rust obsługuje współbieżność, jest częścią języka, wiele rozwiązań współbieżności jest implementowanych jako crate’y. Ewoluują one szybciej niż biblioteka standardowa, więc pamiętaj, aby szukać w Internecie aktualnych, najnowocześniejszych crate’ów do użycia w sytuacjach wielowątkowych.

Biblioteka standardowa Rust zapewnia kanały do przekazywania wiadomości oraz typy wskaźników sprytnych, takie jak Mutex<T> i Arc<T>, które są bezpieczne w użyciu w kontekstach współbieżnych. System typów i sprawdzający pożyczki zapewniają, że kod używający tych rozwiązań nie doprowadzi do wyścigów danych oraz do nieważnych referencji. Gdy kod się skompiluje, możesz być pewien, że będzie działał na wielu wątkach bez trudnych do znalezienia błędów, typowych dla innych języków. Programowanie współbieżne nie jest już pojęciem, którego należy się obawiać: idź i spraw, aby twoje programy były współbieżne, bez strachu!