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

SendSync로 확장 가능한 동시성 만들기

흥미롭게도, 지금까지 이 장에서 다룬 거의 모든 동시성 기능은 언어 자체보다는 표준 라이브러리의 일부였습니다. 즉 동시성을 다루는 방법이 언어나 표준 라이브러리에만 제한되는 것은 아닙니다. 여러분이 직접 동시성 도구를 만들 수도 있고, 다른 사람이 만든 것을 사용할 수도 있습니다.

하지만 표준 라이브러리가 아니라 언어 차원 에 박혀 있는 중요한 동시성 개념으로는 std::markerSendSync 트레이트가 있습니다.

스레드 사이에서 소유권 이전하기

Send 마커 트레이트는, 이 트레이트를 구현한 타입의 값 소유권을 스레드 사이에서 옮기는 것이 안전하다는 뜻입니다. 거의 모든 러스트 타입은 Send 를 구현하지만, 예외도 있습니다. 대표적인 것이 Rc<T> 입니다. Rc<T> 값을 clone 한 뒤 그 clone 의 소유권을 다른 스레드로 넘기면, 두 스레드가 동시에 참조 수를 갱신하려 할 수 있기 때문입니다. 그래서 Rc<T> 는 스레드 안전성 비용을 지불하고 싶지 않은 단일 스레드 환경 전용으로만 구현되어 있습니다.

이 때문에 러스트의 타입 시스템과 트레이트 바운드는, Rc<T> 값을 스레드 사이로 실수로 안전하지 않게 보내는 일을 원천적으로 막아 줍니다. 목록 16-14에서 우리가 실제로 그런 시도를 했을 때, the trait `Send` is not implemented for `Rc<Mutex<i32>>` 라는 오류를 받았습니다. 반대로 Send 를 구현한 Arc<T> 로 바꾸자 코드는 정상적으로 컴파일되었습니다.

다른 모든 구성 요소가 Send 인 타입으로만 이루어진 타입은 자동으로 Send 로 표시됩니다. 거의 모든 기본 타입도 Send 인데, 20장에서 다룰 raw 포인터만은 예외입니다.

여러 스레드에서 접근 허용하기

Sync 마커 트레이트는, 이 트레이트를 구현한 타입에 대해 여러 스레드가 참조하는 것이 안전하다는 뜻입니다. 다시 말해 어떤 타입 TSync 라는 것은, &T (즉 T 에 대한 불변 참조)가 Send 를 구현한다는 말과 같습니다. 그래서 그 참조를 다른 스레드로 안전하게 보낼 수 있습니다. Send 와 비슷하게 기본 타입들은 모두 Sync 를 구현하고, Sync 타입들로만 이루어진 타입 역시 자동으로 Sync 를 구현합니다.

스마트 포인터 Rc<T>Send 가 아니었던 것과 같은 이유로 Sync 도 구현하지 않습니다. 15장에서 다룬 RefCell<T> 와 그 친척 타입들인 Cell<T>Sync 를 구현하지 않습니다. RefCell<T> 가 런타임에 하는 대여 검사 구현은 스레드 안전하지 않기 때문입니다. 반면 Mutex<T> 스마트 포인터는 Sync 를 구현하며, 앞의 “뮤텍스로 접근 제어하기” 절에서 보았듯 여러 스레드가 공유해 사용할 수 있습니다.

SendSync 를 수동 구현하는 것은 unsafe 하다

SendSync 를 구현하는 타입들로만 이루어진 타입은 자동으로 Send, Sync 도 구현하므로, 보통은 우리가 이 트레이트들을 직접 구현할 필요가 없습니다. 또한 이들은 마커 트레이트라서 구현해야 할 메서드조차 없습니다. 동시성과 관련된 어떤 불변 조건을 강제하는 데 쓰이는 표시일 뿐입니다.

이 트레이트들을 수동으로 구현하는 일은 unsafe 러스트 코드를 수반합니다. unsafe 러스트는 20장에서 다루겠지만, 지금 중요한 점은 SendSync 가 아닌 부품들로 새로운 동시성 타입을 만들려면, 안전성 보장을 유지하기 위해 아주 신중한 설계가 필요하다는 것입니다. “The Rustonomicon”에는 이런 보장이 무엇이고 어떻게 지켜야 하는지에 대한 더 많은 정보가 있습니다.

정리

이 책에서 동시성을 보는 것은 아직 여기서 끝이 아닙니다. 다음 장에서는 async 프로그래밍에 집중하고, 21장의 프로젝트에서도 이번 장의 개념을 더 현실적인 상황에서 다시 사용하게 됩니다.

앞에서도 말했듯이, 러스트가 동시성을 다루는 방식 중 언어 자체에 속한 부분은 많지 않습니다. 그래서 많은 동시성 해결책은 크레이트로 구현되어 있습니다. 이런 크레이트는 표준 라이브러리보다 더 빠르게 발전하므로, 멀티스레드 상황에서 최신 기법을 찾고 싶다면 온라인에서 현재 잘 유지되는 크레이트들을 찾아보는 것이 좋습니다.

러스트 표준 라이브러리는 메시지 전달을 위한 채널과, Mutex<T>, Arc<T> 같은 동시성 문맥에서 안전한 스마트 포인터 타입을 제공합니다. 타입 시스템과 대여 검사기는 이런 도구를 사용하는 코드가 데이터 경쟁이나 무효한 참조로 끝나지 않도록 보장합니다. 일단 코드가 컴파일만 된다면, 여러 스레드에서 실행될 때도 다른 언어에서 흔히 보이는 추적하기 어려운 버그 없이 잘 돌아갈 것이라고 믿을 수 있습니다. 이제 동시성은 더 이상 무서워해야 할 개념이 아닙니다. 마음껏, 겁 없이 프로그램을 동시적으로 만들어 보세요!