Skip to content

Commit

Permalink
revise ch16
Browse files Browse the repository at this point in the history
  • Loading branch information
rinthel committed Jun 25, 2023
1 parent b032aeb commit 92a2706
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 55 deletions.
6 changes: 3 additions & 3 deletions src/ch02-00-guessing-game-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

실습 프로젝트를 통해 러스트를 사용해 봅시다. 이번 장은 실제 프로젝트에서 몇몇
일반적인 러스트의 개념이 어떻게 활용되는지를 소개하려 합니다. 이 과정에서 `let`,
`match`, 메서드, 연관함수 (associated functions), 외부 크레이트 (external crates)
`match`, 메서드, 연관 함수 (associated functions), 외부 크레이트 (external crates)
등의 활용 방법을 배울 수 있습니다. 이런 개념들은 다음 장들에서 더 자세히 다뤄질
것입니다. 이번 장에서는 여러분이 직접 기초적인 내용을
실습합니다.
Expand Down Expand Up @@ -172,7 +172,7 @@ let mut bananas= 5; // mutable

프로그램에 첫 번째 라인에 `use std::io;`를 이용하여 표준
라이브러리의 입출력 기능을 가져온 것을 상기해 보세요. 이제
`io` 모듈의 연관함수인 `stdin`을 호출하는데, 이것이 사용자의
`io` 모듈의 연관 함수인 `stdin`을 호출하는데, 이것이 사용자의
입력을 처리할 수 있게 해 줄 것입니다:

```rust,ignore
Expand Down Expand Up @@ -909,7 +909,7 @@ You win!

## 정리

이 프로젝트는 `let`, `match`, 메서드, 연관함수, 외부 크레이트 사용과
이 프로젝트는 `let`, `match`, 메서드, 연관 함수, 외부 크레이트 사용과
같이 많은 새로운 러스트 개념들을 소개하기 위한 실습이었습니다. 다음
장들에서는 이 개념들의 세부적인 내용을 배울 것입니다. 3장은 대부분의
프로그래밍 언어들이 가지고 있는 변수, 데이터 타입, 함수를 소개하고
Expand Down
2 changes: 1 addition & 1 deletion src/ch05-00-structs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
것입니다.

우선 구조체를 정의하고 생성하는 방법을 살펴봅니다. 그리고 구조체의
데이터와 연관된 동작을 표현하는 *메서드*연관함수 (associated functions)
데이터와 연관된 동작을 표현하는 *메서드*연관 함수 (associated functions)
정의 방법을 다루겠습니다. 구조체와 열거형(6장에서 설명할 예정입니다)은
프로그램 도메인에 새로운 타입을 만들어서 러스트의 컴파일 시점
타입 검사 기능을 최대한 활용하기 위한 기본 구성 요소입니다.
80 changes: 40 additions & 40 deletions src/ch16-03-shared-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

메시지 패싱은 동시성을 다루는 좋은 방법이지만, 유일한 수단은
아닙니다. 또다른 방법은 여러 스레드가 동일한 공유 데이터에 접근하는
것입니다. Go 언어 문서로부터 나온 슬로건의 일부를 다시한번 고려해보죠:
‘메모리를 공유하여 통신하지 마세요.’
것입니다. 고 (Go) 언어 문서로부터 나온 슬로건의 일부를 다시 한번 생각해
보세요: ‘메모리를 공유하여 통신하지 마세요.’

메모리를 공유하는 통신은 어떻게 생겼을까요? 더불어서 메시지 패싱 애호가들은
왜 메모리 공유를 쓰지 말라고 경고할까요?

어떤 면에서, 모든 프로그래밍 언어의 채널들은 단일 소유권과 유사한데,
이는 값이 채널로 송신되면, 그 값은 더이상 쓸 수 없게 되기 때문입니다.
이는 값이 채널로 송신되면, 그 값은 더 이상 쓸 수 없게 되기 때문입니다.
공유 메모리 동시성은 복수 소유권과 유사합니다: 여러 스레드들이 동시에
동일한 메모리 위치를 접근할 수 있지요. 스마트 포인터가 복수 소유권을
가능하게 하는 내용을 담은 15장에서 보셨듯이, 복수 소유권은 서로 다른
Expand All @@ -20,10 +20,10 @@

### 뮤텍스를 사용하여 한번에 한 스레드에서의 데이터 접근을 허용하기

*뮤텍스**상호 배제 (mutual exclusion)*줄임말로서, 뮤텍스에서는
*뮤텍스**상호 배제 (mutual exclusion)*줄임말로, 뮤텍스에서는
한번에 하나의 스레드만 데이터 접근을 허용합니다. 뮤텍스 내부의 데이터에
접근하기 위해서 스레드는 먼저 뮤텍스의 *락 (lock)* 을 얻는 요청을 하는 것으로
접근을 원한다는 신호를 보내야 합니다. 락은 누가 현재 배타적으로 데이터에 접근하는지
접근하려면 스레드는 먼저 뮤텍스의 *락 (lock)* 을 얻는 요청을 해서
접근을 희망하는 신호를 보내야 합니다. 락은 누가 현재 배타적으로 데이터에 접근하는지
추적하는 뮤텍스의 일부에 해당하는 데이터 구조입니다. 그러므로, 뮤텍스는 잠금
시스템을 통해 가지고 있는 데이터를 *보호하는 (guard)* 것으로 묘사됩니다.

Expand All @@ -40,16 +40,16 @@
때는 원하는 만큼 길게 말한 다음, 말하기를 원하는 다음 패널 참가자에게
마이크를 건네줍니다. 만일 패널 참여자가 마이크 사용을 끝냈을 때
이를 건네주는 것을 잊어먹는다면, 그 외 아무도 말할 수 없게 됩니다.
공유하는 마이크의 관리가 잘못되면, 패널 토의는 계획된데로 되지
않을겁니다!
공유하는 마이크의 관리가 잘못되면, 패널 토의는 계획대로 진행되지
않을 겁니다!

뮤텍스의 관리를 올바르게 하려면 믿을 수 없을만큼 까다로울 수 있는데, 이것이 바로
많은 사람들이 채널 애호가가 되는 이유입니다. 하지만, 러스트의 타입 시스템과
소유권 규칙에 덕분에 락과 언락이 잘못 될 수는 없습니다.

#### `Mutex<T>`의 API

뮤텍스 사용 방법에 대한 예제로서 예제 16-12처럼 싱글스레드 컨텍스트에서
뮤텍스 사용 방법에 대한 예제로, 예제 16-12처럼 싱글스레드 컨텍스트에서
뮤텍스를 사용하는 것으로 시작해봅시다:

<span class="filename">파일명: src/main.rs</span>
Expand All @@ -61,31 +61,31 @@
<span class="caption">예제 16-12: 간소화를 위해 싱글스레드 컨텍스트에서
`Mutex<T>`의 API 탐색하기</span>

많은 타입들이 그렇듯 `Mutex<T>`연관함수 `new`를 사용하여 만들어집니다.
많은 타입이 그렇듯 `Mutex<T>`연관 함수 `new`를 사용하여 만들어집니다.
뮤텍스 내의 데이터에 접근하기 위해서는 `lock` 메서드를 사용하여 락을
얻습니다. 이 호출은 현재의 스레드를 블록할 것이므로, 락을 얻을 차례가
될 때까지 아무런 작업도 할 수 없습니다.

`lock`의 호출은 락을 가진 다른 스레드가 패닉인 경우 실패할 것입니다.
`lock`의 호출은 락을 가진 다른 스레드가 패닉 상태인 경우 실패할 것입니다.
그런 경우 아무도 락을 얻을 수 없게 되므로, `unwrap`을 택하여 그런 상황일
경우 이 스레드에 패닉을 일으킵니다.

락을 얻고난 후에는 그 반환 값, 지금의 경우 `num`이라는 이름의 값을 내부
데이터에 대한 가변 참조자로서 다룰 수 있습니다. 타입 시스템은 `m` 내부의
데이터에 대한 가변 참조자로 취급할 수 있습니다. 타입 시스템은 `m` 내부의
값을 사용하기 전에 락을 얻도록 보장합니다. `Mutex<i32>``i32`가 아니므로
`i32` 값을 사용하기 위해서는 *반드시* 락을 얻어야 합니다. 잊어먹을 수가
없습니다; 잊어버린다면 타입 시스템이 내부의 `i32`에 접근할 수 없게 할
것입니다.

의심했을지 모르지만 `Mutex<T>`는 스마트 포인터입니다. 더 정확하게는 `lock`
짐작하셨을지 모르겠지만 `Mutex<T>`는 스마트 포인터입니다. 더 정확하게는 `lock`
호출이 `MutexGuard`라는 스마트 포인터를 *반환하는데,* `unwrap` 호출을 통해
처리되는 `LockResult`로 감싸져 있습니다. `MutexGuard` 스마트 포인터는
내부 데이터를 가리키도록 `Deref`가 구현되어 있습니다; 또한 `MutexGuard` 스마트
포인터에는 `Drop` 구현체가 있는데, 이것으로 내부 스코프의 끝에서 스코프
밖으로 벗어났을 때 자동으로 락을 해제하는 일이 벌어집니다. 결과적으로 락이
자동으로 해제되기 때문에, 우리는 락을 해제하는 것을 잊어버리고 다른
스레드에 의해 뮤텍스가 사용되는 것을 막는 위험을 감수하지 않아도
됩니다.
밖으로 벗어났을 때 자동으로 락을 해제하는 일이 벌어집니다. 결과적으로
락이 자동으로 해제되기 때문에, 락을 해제하는 것을 잊어버려 다른
스레드에서 뮤텍스가 사용되지 못하게 차단될 위험이
없습니다.

락이 버려진 후에는 뮤텍스 값을 출력하여 내부의 `i32`를 6으로 바꿀 수 있음을
확인할 수 있습니다.
Expand All @@ -94,7 +94,7 @@

이제 `Mutex<T>`를 사용하여 여러 스레드 사이에서 값을 공유하는 시도를 해봅시다.
10개의 스레드를 생성하고 각자 카운터 값을 1씩 증가시켜서 카운터가 0에서 10으로
가도록 할 것입니다. 다음 예제 16-13의 예제는 컴파일 에러가 날 것이고,
가도록 할 것입니다. 다음 예제 16-13는 컴파일 에러가 날 것이고,
이 에러를 이용하여 `Mutex<T>`를 사용하는 방법과 러스트가 이를 고치는 것을
돕는 방법에 대해 학습하겠습니다.

Expand Down Expand Up @@ -134,7 +134,7 @@
#### 복수 스레드와 함께하는 복수 소유권

15장에서는 스마트 포인터 `Rc<T>`을 사용하여 참조 카운팅 값을 만들어
값에 복수의 소유자를 부여했습니다. 여기서도 똑같이 해서 어떻게 되는지
값에 여러 소유자를 부여했습니다. 여기서도 똑같이 해서 어떻게 되는지
봅시다. 예제 16-14의 `Mutex<T>``Rc<T>`로 감싸서 스레드로 소유권을
넘기기 전에 그 `Rc<T>`을 복제하겠습니다.

Expand All @@ -147,44 +147,44 @@
<span class="caption">예제 16-14: `Rc<T>`를 사용하여 여러 스레드가
`Mutex<T>`를 소유할 수 있도록 하는 시도</span>

다시 한번 컴파일을 하고 그 결과가... 다른 에러들이네요! 컴파일러는 우리에게
많은 것을 가르치고 있습니다.
다시 한번 컴파일을 하고 그 결과가... 다른 에러들이네요! 컴파일러는
많은 것을 가르쳐 주고 있습니다.

```console
{{#include ../listings/ch16-fearless-concurrency/listing-16-14/output.txt}}
```

와우, 이 에러는 정말 장황하네요! 여기 집중할 중요한 부분이 있습니다:
`` `Rc<Mutex<i32>>`는 스레드간에 안전하게 보내질수 없습니다 ``.
또한 컴파일러는 그 이유를 말해주고 있습니다: `` 트레이트 `Send``Rc<Mutex<i32>>`
에 대해 구현되지 않았습니다 ``. `Send`에 대해서는 다음 절에서 이야기하겠습니다:
이것은 스레드와 함께 사용하는 타입들이 동시적 상황에서 쓰이기 위한 것임을
보장하는 트레이트 중 하나입니다.
와우, 이 에러는 정말 장황하네요! 여기 집중할 중요한 부분이 있습니다: `` `Rc<Mutex<i32>>`
cannot be sent between threads safely (`Rc<Mutex<i32>>`는 스레드간에 안전하게 보낼 수
없습니다)``. 또한 컴파일러는 그 이유를 말해주고 있습니다: ``the trait `Send` is not
implemented for `Rc<Mutex<i32>>` (트레이트 `Send``Rc<Mutex<i32>>` 에 대해 구현되지
않았습니다)``. `Send`에 대해서는 다음 절에서 이야기하겠습니다: 이것은 스레드와 함께 사용하는
타입들이 동시적 상황에서 쓰이기 위한 것임을 보장하는 트레이트 중 하나입니다.

안타깝게도, `Rc<T>`는 스레드를 교차하면서 공유하기에는 안전하지 않습니다. `Rc<T>`
참조 카운트를 관리할 때, 각각의 `clone` 호출마다 카운트에 더하고 각 클론이
안타깝게도, `Rc<T>`는 스레드를 교차하면서 공유하기에는 안전하지 않습니다.
`Rc<T>`참조 카운트를 관리할 때, `clone` 호출마다 카운트에 더하고 각 클론이
버려질 때 카운트에서 제합니다. 하지만 그것은 다른 스레드에 의해 카운트를
변경하는 것을 방해할 수 없음을 보장하는 어떠한 동시성 기초 재료도 이용하지
않습니다. 이는 잘못된 카운트를 야기할 수 있습니다-결과적으로 메모리 누수를
발생시키거나 아직 다 쓰기 전에 값이 버려질 수 있는 미세한 버그를 낳겠죠.
우리가 원하는 것은 정확히 `Rc<T>`와 비슷하지만 스레드-안전한 방식으로
참조 카운트를 바꾸는 녀석입니다.

#### `Arc<T>`를 이용한 아토믹 (atomic) 참조 카운팅
#### `Arc<T>`를 이용한 아토믹 참조 카운팅

다행히도, `Arc<T>`*바로* 동시적 상황에서 안전하게 사용할 수 있는 `Rc<T>`
같은 타입입니다. *a**아토믹 (atomic)* 을 의미하는데, 즉 이것이 *원자적으로
참조자를 세는 (atomically reference counted)* 타입임을 뜻합니다. 아토믹은
여기서는 자세히 다루지 않을 추가적인 동시성 기초 재료 종류입니다: 더 자세히
추가적인 종류의 동시성 기초 재료로서, 여기서는 자세히 다루지 않을 겁니다: 더 자세히
알고 싶으면 [`std::sync::atomic`][atomic]<!-- ignore -->에 대한 표준
라이브러리 문서를 보세요. 이 시점에서는 아토믹이 기초 타입처럼 동작하지만
스레드를 교차하며 공유해도 안전하다는 것만 알면 됩니다.

그렇다면 여러분은 왜 모든 기초 타입이 아토믹하지 않은지, 그리고 표준 라이브러리
타입은 왜 기본적으로 `Arc<T>`을 구현에 이용하지 않는지를 궁금해 할지도 모르겠습니다.
그 이유는 스레드 안전성이란 것이 정말로 필요할 때만 감내하고 싶을 성능 저하를
일으키기 때문입니다. 만일 싱글스레드 내에서만 값을 연산하는 중이라면,
아토믹이 제공하는 보장을 강제할 필요가 없다면 코드는 더 빠르게 실행될
그 이유는 스레드 안전성이란 것이 정말로 필요할 때만 감내하고 싶을 성능
저하를 일으키기 때문입니다. 싱글스레드 내에서만 값을 연산하는 경우,
아토믹이 제공하는 보장을 강제할 필요없이 코드는 더 빠르게 실행될
수 있습니다.

예제로 다시 돌아갑시다: `Arc<T>``Rc<T>`는 같은 API를 가지고 있으므로,
Expand Down Expand Up @@ -225,20 +225,20 @@ Result: 10

### `RefCell<T>`/`Rc<T>``Mutex<T>`/`Arc<T>` 간의 유사성

`counter` 불변이지만 내부값에 대한 가변 참조자를 가지고 올 수 있었음을
알아챘을지 모르겠습니다; 이는 `Cell` 가족이 그러하듯 `Mutex<T>`가 내부
`counter` 불변이지만 내부값에 대한 가변 참조자를 가지고 올 수 있었음을
알아채셨을 수도 있겠습니다; 이는 `Cell` 가족이 그러하듯 `Mutex<T>`가 내부
가변성을 제공한다는 의미입니다. 15장에서 `Rc<T>`의 내용물을 변경할 수
있도록 하기 위해 `RefCell<T>`을 사용한 것과 같은 방식으로, `Arc<T>` 내부의
값을 변경하기 위해 `Mutex<T>`를 이용합니다.

주목할만한 또다른 세부 사항은 `Mutex<T>`를 사용할 때 러스트가 모든 종류의
논리 에러로부터 보호해줄 수 없다는 것입니다. 15장에서 `Rc<T>`를 사용하는
것은 두 `Rc<T>` 값들이 서로를 참조하여 메모리 누수를 야기하는 순환 참조자를 만들
위험성이 따라오는 것이었음을 상기하세요. 이와 유사하게, `Mutex<T>`에는
위험성이 따라오는 것이었음을 상기해 봅시다. 이와 유사하게, `Mutex<T>`에는
*데드락 (deadlock)* 을 생성할 위험성이 따라옵니다. 이것은 어떤 연산이 두 개의
리소스에 대한 락을 얻을 필요가 있고 두 개의 스레드가 하나씩의 락을 얻는다면,
서로가 서로를 영원히 기다리는 식으로 발생됩니다. 데드락에 흥미가 있다면,
데드락이 있는 러스트 프로그램 만들기를 시도해보세요; 그리고나서 아무 언어에 있는
리소스에 대한 락을 얻을 필요가 있고 두 개의 스레드가 락을 하나씩 얻는다면,
서로가 서로를 영원히 기다리는 형태로 발생됩니다. 데드락에 흥미가 있다면,
데드락이 있는 러스트 프로그램 만들기를 시도해보세요; 그다음 아무 언어에 있는
뮤텍스를 위한 데드락 완화 전략를 연구해보고 이를 러스트에서 구현해보세요.
`Mutex<T>``MutexGuard`에 대한 표준 라이브러리 API 문서가 유용한
정보를 제공합니다.
Expand Down
Loading

0 comments on commit 92a2706

Please sign in to comment.