티스토리 뷰

반응형

동시성과 병렬성을 헷갈리지 말자

둘 이상의 코드 조각이 실행될 때

  • 동시성 (Concurrency): 동시에 실행 중인 것처럼 행동하는 것
  • 병렬성 (Parallelism): 실제로 동시에 실행되는 것

 

동시성을 얻으려면

  • 실행 중의 코드의 다른 부분으로 실행을 전환할 수 있어야 함.
  • Fiber, Thread, Process 등을 이용해 구현

 

병렬성을 얻으려면

  • 2가지 일을 동시에 할 수 있는 하드웨어가 필요함.
  • 여러 개의 코어, 여러 CPU, 네트워크에 연결된 여러 대의 컴퓨터 등을 이용해 구현

 

 

모든 일엔 동시성이 있다

시스템의 규모가 어느정도 넘어가면 동시성을 고려하지 않고 코드를 작성하는 것이 거의 “불가능”

  • 코드 짤 때 이런게 잘 안보인다면, 라이브러리/프레임워크 안에 숨겨져 있는 경우일 것
  • 실제 세상이 비동기적이기에 실제 세상을 다기 원한다면 동시성은 필수임

 

동시성/병렬성 있는 코드를 작성하는 것은 어렵다?

  • 왜 그럴까? 보통 우리는 프로그래밍을 순차적 시스템으로 배우기 때문. A하고 B하자.
  • 보통 프로그래밍 언어에서 순차적으로 사용할 때는 비교적으로 안전하지만, 동시에 두 가지 일이 일어날 수 있으면 골칫거리로 변한다 → 가장 큰 예는 공유상태

 

 

Topic 33. 시간적 결합 깨트리기

시간적 결합 (Temporal Coupling)

  • 실행 순서가 중요하지 않은 작업 간, 순서가 강제될 때 생기는 결합

 

코드를 작성하며 시간에서 우리가 신경써야 할 측면 2가지

  1. 동시성 (동시에 일어나는 일)
  2. 순서 (시간의 흐름속에서 일들의 상대적인 위치)

 

보통 코드를 작성할 때, 두 측면 모두 특별히 신경쓰지 않는 경우가 많다.

  • 보통 직선적인 사고를 하게 된다. ‘이것을 한 뒤, 저것을 하고…’, ‘A 메서드 뒤에 B 메서드 동작시키고 ...’
  • 이런 식으로 생각하다보면 순서가 필요없는 작업임에도 “시간적 결합”을 만들게 된다.
  • 이러한 사고 방법은 그다지 유연하지 않고, 현실과 동떨어진다.
  • 이러한 시간적 결합을 끊어야 한다. → 유연성을 얻고, 아키텍처/설계/배포 같은 시간 관련 의존성을 줄일 수 있다.

 

 

동시성 찾기

활동 다이어그램 같은 표기법을 이용해 작업 흐름을 기록해보자.

https://www.visual-paradigm.com/guide/uml-unified-modeling-language/what-is-activity-diagram/

 

 

순서가 필요한 작업이 어디인지, 병렬적으로 실행할 수 있는 부분이 어디인지 파악할 수 있다.

  • 병렬성이 필요한 곳을 찾아 병렬성을 극대화 시킬 수 있다.

 

그럼 동시작업이 가능한 곳에 다 동시성을 부여해야할까?

  • 리소스가 가능하다면 좋겠지만, 현실은 쉽지 않다. 즉, 동시성 설계를 할 곳을 찾아야 한다.
  • 우리 코드가 아닌 곳에서 시간이 걸리는 곳이 적합하다.
    • 외부 서비스 접근 (데이터베이스 접근 등), 사용자 입력 기다릴 때 등
  • 이는, CPU가 쉬는 대신 좀 더 생산적인 업무를 할 수 있게 해준다.

 

그럼, 병렬작업은?

  • 여러 개의 프로세서/코어가 필요
  • 독립적인 부분의 작업이 있는 곳이 적합하다.
    • 다른 부분작업을 기다릴 필요 없이, 쪼개서 병렬로 처리하고 이를 합치는 과정
  • 엘릭서(Elixir) 컴파일러
    • 빌드하는 프로젝트를 여러 모듈로 쪼갠 후, 각각 병렬로 컴파일
    • 모듈 간 의존하는 케이스가 있다면, 선행되어야 하는 모듈 빌드 후 진행됨
    • 그렇기에 빠른 컴파일이 가능하다

 

 

Topic 34. 공유 상태는 틀린 상태

가게 주문 예시

  • 여러 테이블에서 동시에 애플파이 주문이 들어옴.
  • 애플파이는 냉장고에 1개 남은 상태.
  • 각 테이블의 종업원은 냉장고를 보고 주문을 받음.
  • 한 테이블을 제외하곤 받지 못하게 될 것.

 

문제는 상태가 공유된 것

  • 종업원들은 서로를 고려하지 않고 냉장고만 확인했다.

 

비-원자적 갱신

  • 파이 조각을 가져오고 갱신하는 동작이 원자적(atomic)이지 않았기 때문.
  • 원자적으로 어떻게 변경할 수 있을까?

 

세마포어 및 상호배제 방법들

세마포어(Semaphore) - 한 번에 한 사람만 가질 수 있는 무언가

  • 냉장고 사용을 제어하는데 세마포어를 이용 → 세마포어를 소유한 사람만이 냉장고 상태를 바꿀 수 있게.
  • 세마포어를 도깨비 인형이라고 해보자.
    • 종업원은 주문을 승낙하기 전에 도깨비 인형을 가져가고 주문 승낙, 파이 전달 후 다시 도깨비 인형을 제자리로 돌려놓는다.

 

세마포어 이용 시, 주의 사항

  • 세마포어를 소유한 쪽에서 어느 경우에도 ‘반환’이 될 수 있도록 구성되어야 한다.
  • 보통 구현 시 실수를 한다. 개념을 이해한 후, 잘 만들어진 라이브러리를 활용해보자.

 

트랜잭션이 없는 갱신

말 그대로 기록이 없는 갱신 (기록을 알아채지 못한 갱신 이라고도 할 수 있겠다)

공유 메모리는 동시성 문제의 원인으로 많이 지목받음

  • 공유 메모리까지 아니더라도 수정 가능한 리소스를 공유하는 코드 어디서나 동시성 문제는 발생함.
  • ‘불규칙한 실패’는 동시성 문제인 경우가 많다. 먼저 의심해보자.

 

그 밖의 독점적인 접근

대부분 언어에는 공유 리소스에 독점적으로 접근하는 것을 도와주는 라이브러리가 있음

  • 상호 배제(Mutual exclusion), 뮤텍스(Mutex), 세마포어(Semaphore), 모니터(Monitor)

언어 자체에 동시성 지원이 잇는 경우도 있음 → 러스트 (Rust)

  • ‘소유권(Ownership)’이라는 개념 → 단 하나의 변수나 매개변수만이 데이터 조각에 접근할 수 있게 강제.

 

 

소유권이 뭔가요? - The Rust Programming Language

러스트의 핵심 기능은 바로 소유권입니다. 이 기능은 직관적으로 설명할 수 있지만, 언어의 나머지 부분에 깊은 영향을 끼칩니다. 모든 프로그램은 실행하는 동안 컴퓨터의 메모리를 사용하는

rinthel.github.io

 

 

Topic 35. 액터와 프로세스

액터 - 자신만의 비공개 지역 상태(State)를 가진 독립적인 가상 처리 장치(Virtual process)

  • 우편함이라고 보자. 액터가 잠자고 있을 때, 우편함에 메시지가 도착하면 액터가 깨어나면서 메시지를 처리. 우편함이 비어있으면 다시 잠든다.
  • 메시지 처리할 때, 다른 액터를 생성하거나, 아는 액터에게 메시지를 보내거나, 새로운 상태를 생성할 수 있다.

 

액터는 언제나 동시성을 가짐

  • 액터를 관리하는 것 없음. 과정 조율하는 것 없음.
  • 시스템이 저장하는 것은 오직 메시지와 액터의 지역 상태.
    • 메시지는 수신자가 읽어 확인하는 법 밖에 없고, 지역상태는 액터 바깥에서 접근이 불가능.
  • 모든 메시지는 일방향. 답장이란 개념이 없다. 답장의 측면이 아닌 새로운 메시지를 보내는 측면으로 이해해야 함.
  • 액터는 한 번에 하나의 메시지만 처리. 중간에 다른 일을 하지 않음.

 

액터는 아무것도 공유하지 않으면서 비동기적으로 동시에 실행됨

  • 물리적인 리소스(프로세서 등)가 충분하다면 여러 액터를 동시에 돌릴 수 있을 것. 만약 프로세스가 1개라면, 액터마다 컨텍스트를 전환해가며 실행(동시성)시킬 수 있을 것.

 

그러므로, 액터에서는 동시성을 다루는 코드를 쓸 필요가 없다.

  • 고유된 상태 자체가 없기 때문.
  • “이걸 한 다음, 저걸 해” 라는 개념의 로직을 작성할 필요도 없다. 단순히 메시지를 수신/발행 만 존재할 뿐.

 

얼랭 언어, 런타임은 액터 구현의 좋은 사례

  • 프로세스가 가벼워 수백만 개를 실행시킬 수 있고, 프로세스(액터를 프로세스라 부름)간 메시지를 통해 통신함. 프로세스 간 격리되어 있어 상태를 공유하지 않는다.
  • 가장 신뢰성 높은 시스템들에 사용되며, 99.999999% 가용성을 자랑함.
  • 얼랭의 후손인 Elixir도 액터의 개념을 사용하고 있고, 다양한 언어에서 구현체들이 존재함.

 

Java 계열(JVM)에선 다음과 같은 구현체가 있고, 많이 사용된다.

  • Akka
  • Vert.x
  • 코루틴 액터 개념?

 

Topic 36. 칠판

칠판에 정보를 넣고, 누군가는 그 정보를 가져가 사용하는 프로세스.

  • 정보를 가져가서 적절히 조합해 다시 칠판에 넣을 수도 있다.
  • 데이터의 도착 순서를 생각할 필요 없이, 규칙 엔진을 넣어 특정 데이터가 왔을 때 규칙이 실행되도록 할 수 있음 (파이프라인)

 

메시징 시스템과 유사성

Kafka, NATS 같은 메시징 시스템들.

  • 이벤트 로그의 형태를 영속화 함. 패턴 매칭 형태로 메시지도 가져올 수 있음.
  • 즉, 메시징 시스템을 칠판으로 이용할 수도 있고액터를 실행하는 플랫폼으로 사용할 수도 있음.

 

간단하진 않다.

많은 동작이 간접적으로 발생 → 분석이 어렵다.

어떻게 해결할까? 어떤게 도움이 될까?

  • 메시지 형식, API를 모아두는 중앙 저장소 운영
  • 작업 처리시 고유한 추적 아이디(Trace id)를 추가 → 메시지/정보 추적, 재구성
320x100
반응형
댓글
반응형
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함