두려움 없는 동시성

동시성 프로그래밍을 안전하고 효율적으로 처리하는 것은 Rust의 주요 목표 중 하나다. 동시성 프로그래밍은 프로그램의 여러 부분이 독립적으로 실행되는 것을 의미하며, 병렬 프로그래밍은 프로그램의 여러 부분이 동시에 실행되는 것을 의미한다. 이 두 개념은 컴퓨터가 멀티 프로세서의 이점을 활용하면서 점점 더 중요해지고 있다. 역사적으로, 이러한 프로그래밍 방식은 어렵고 오류가 발생하기 쉬웠다. Rust는 이를 바꾸고자 한다.

처음에 Rust 팀은 메모리 안전성을 보장하는 것과 동시성 문제를 해결하는 것이 서로 다른 방법으로 해결해야 하는 두 가지 과제라고 생각했다. 그러나 시간이 지나면서, 팀은 소유권과 타입 시스템이 메모리 안전성과 동시성 문제를 모두 관리하는 강력한 도구라는 사실을 발견했다. 소유권과 타입 검사를 활용함으로써, 많은 동시성 오류가 런타임 오류가 아니라 컴파일 타임 오류로 발생한다. 따라서 런타임 동시성 버그가 발생하는 정확한 상황을 재현하기 위해 많은 시간을 들이지 않고도, 잘못된 코드는 컴파일을 거부하고 문제를 설명하는 오류를 표시한다. 결과적으로, 코드를 프로덕션에 배포하기 전에 작업 중에 문제를 해결할 수 있다. Rust의 이러한 측면을 두려움 없는 동시성이라고 부른다. 두려움 없는 동시성은 미묘한 버그 없이 코드를 작성할 수 있게 해주며, 새로운 버그를 도입하지 않고도 리팩토링하기 쉽게 만든다.

참고: 간단히 설명하기 위해, 많은 문제를 동시성이라고만 언급할 것이다. 하지만 이 장에서는 동시성 및/또는 병렬성이라는 표현을 염두에 두고 읽어주길 바란다. 다음 장에서는 이 둘의 차이를 더 구체적으로 다룰 것이다.

많은 언어들은 동시성 문제를 해결하기 위해 특정한 해결책만 제공한다. 예를 들어, Erlang은 메시지 전달 동시성을 위한 우아한 기능을 제공하지만, 스레드 간 상태를 공유하는 방법은 명확하지 않다. 가능한 해결책의 일부만 지원하는 것은 고수준 언어에게는 합리적인 전략이다. 왜냐하면 고수준 언어는 일부 제어를 포기함으로써 추상화의 이점을 얻기 때문이다. 그러나 저수준 언어는 주어진 상황에서 최고의 성능을 제공하는 해결책을 제공해야 하며, 하드웨어에 대한 추상화가 적다. 따라서 Rust는 상황과 요구 사항에 맞는 다양한 도구를 제공한다.

이 장에서 다룰 주제는 다음과 같다:

  • 여러 코드 조각을 동시에 실행하기 위해 스레드를 생성하는 방법
  • 메시지 전달 동시성: 채널을 통해 스레드 간 메시지를 전송하는 방법
  • 공유 상태 동시성: 여러 스레드가 어떤 데이터에 접근할 수 있는 방법
  • SyncSend 트레이트: Rust의 동시성 보장을 사용자 정의 타입과 표준 라이브러리 제공 타입에까지 확장하는 방법