Chapter 6

동시성

  • 6.1 시간적 결합 깨트리기
  • 6.2 공유 상태는 틀린 상태
  • 6.3 액터와 프로세스
  • 6.4 칠판

동시성(concurrency) 이야기야. 현대 소프트웨어에서 동시성을 피할 수 없거든. 멀티코어 CPU, 분산 시스템, 비동기 I/O — 순차적으로만 생각하면 한계에 부딪히지. 20주년 기념판에서 가장 현실적으로 업데이트된 장이기도 해.

**시간적 결합(temporal coupling)**이란 "A가 끝나야 B를 시작할 수 있다"는 암묵적 의존성이야. 이게 왜 문제냐면, 실제로는 A와 B가 동시에 실행될 수 있는데도 순서를 강제하면 성능을 낭비하기 때문이지.

저자들이 제안하는 도구: **활동 다이어그램(activity diagram)**을 그려봐.

작업 흐름을 그려보면 어떤 작업이 병렬로 실행 가능한지 보여. 아침에 일어나서 커피 내리면서 토스트 굽는 것처럼 — 커피가 다 내려질 때까지 멍하니 서 있을 필요 없잖아.

동시성을 고려한 설계의 핵심:

  • 작업 간 의존 관계를 명시적으로 파악해
  • 독립적인 작업은 병렬로 실행할 수 있게 설계해
  • 순서가 필요한 곳에만 순서를 강제해

동시성은 병렬성(parallelism)과 달라. 동시성은 "동시에 다룰 수 있는 것들의 구조"이고, 병렬성은 "실제로 동시에 실행하는 것"이야. 싱글 코어에서도 동시성 설계는 의미가 있지.

공유 메모리로 통신하지 말고, 통신으로 메모리를 공유해. Go 언어의 유명한 격언인데, 이 책에서도 같은 원칙을 말하거든.

두 프로세스가 같은 데이터를 읽고 쓰면 문제가 생기잖아. 경쟁 조건(race condition), 교착 상태(deadlock), 일관성 깨짐 — 동시성 버그의 클래식이지.

파이 가게 비유가 나와. 진열장에 파이가 하나 남았고, 두 명이 동시에 "파이 있나요?"를 물어봤어. 둘 다 "네"라는 답을 듣고 주문하지. 하지만 파이는 하나뿐이라 한 명은 실망해. "확인하는 것과 행동하는 것 사이에 틈이 있으면" 이런 일이 생기는 거야.

해결 방법들:

  • 세마포어/뮤텍스 — 임계 영역을 잠그는 고전적 방법. 작동하지만 실수하기 쉽고 데드락 위험이 있지
  • 불변 데이터 — 아예 바꿀 수 없게 만들면 공유해도 안전해
  • 액터 모델 — 각자 독립된 상태를 갖고 메시지로만 통신
  • 트랜잭셔널 메모리 — 데이터베이스 트랜잭션처럼 원자적 갱신

저자들의 기본 입장: 공유 상태를 최소화해. 가능하면 없애.

액터 모델은 동시성 문제를 우아하게 해결하는 패턴이야.

액터란:

  • 독립된 상태를 가진 독립된 실행 단위
  • 다른 액터와는 오직 비동기 메시지로만 통신
  • 메시지를 받으면 처리하고, 필요하면 다른 액터에게 메시지를 보내지
  • 잠금(lock)이 필요 없어 — 각자 자기 상태만 관리하니까

Erlang이 이 모델의 대표 주자야. "수십만 개의 가벼운 프로세스가 메시지를 주고받는" 구조로 전화 교환기 같은 고가용성 시스템을 만들어냈거든.

액터 모델의 장점:

  • 공유 상태가 없으니 경쟁 조건이 원천 차단돼
  • 분산 시스템으로 자연스럽게 확장 — 메시지가 네트워크를 타면 분산 시스템이지
  • 장애 격리 — 하나의 액터가 죽어도 다른 액터는 영향 없어

실무에서 쓸 수 있는 구현: Erlang/Elixir의 OTP, Akka(Java/Scala), Rust의 actix 등.

칠판(Blackboard) 시스템은 독특한 동시성 패턴이야.

비유: 형사들이 사건을 수사할 때 칠판에 단서를 적어놓지. 아무 형사나 와서 칠판을 보고, 새로운 단서를 추가하고, 기존 단서를 조합해서 새로운 추론을 해. 형사들끼리 직접 대화할 필요 없이 칠판을 통해 협업하는 셈이야.

소프트웨어에서 칠판 시스템:

  • 공유 작업 공간이 있어
  • 독립적인 프로세스들이 데이터를 읽고 쓰지
  • 프로세스 간 직접 의존성이 없어
  • 새로운 데이터가 추가되면 관련 프로세스가 활성화돼

워크플로 시스템, 규칙 엔진 같은 곳에서 유용하지. Apache Kafka 같은 메시지 브로커도 칠판의 변형으로 볼 수 있어.

칠판 시스템의 실용적 주의점: 디버깅이 어려워. 누가 언제 무슨 데이터를 썼는지 추적하기 어려우니 로깅과 모니터링을 철저히 해야 하거든.


정리

6장 읽고 기억할 거 세 가지:

  1. 시간적 결합을 찾아서 깨. 동시에 할 수 있는 걸 순서대로 할 이유는 없잖아
  2. 공유 상태는 악의 근원이야. 줄이거나, 불변으로 만들거나, 메시지 패싱으로 대체해
  3. 액터 모델은 동시성의 강력한 해법이지. 독립된 상태 + 비동기 메시지 = 잠금 없는 동시성이야