ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 실용주의 프로그래머 - 6장 동시성
    Study 2023. 7. 22. 16:01
    • 동시성(concurrency) : 둘 이상의 코드 조각이 실행될 때 동시에 실행 중인 것처럼 행동하는 것
    • 병렬성(parallelism) : 실제로 동시에 실행되는 것
    • 동시성을 얻으려면 실행 중에 코드의 다른 부분으로 실행을 전환할 수 있는 환경에서 코드를 구동해야 한다. 보통은 파이버(fiber)나 스레드, 프로세스 등을 사용하여 동시성을 구현한다.
    • 병렬성을 얻으려면 두 가지 일을 동시에 할 수 있는 하드웨어가 필요하다.
    • 애플리케이션이 실제 세상을 다루기 원한다면 동시성은 필수다. 세상은 비동기적이기 때문이다.
    • 가장 큰 문제는 '공유 상태(shared state)'다. 둘 이상의 코드 뭉치가 하나의 변경 가능한 데이터를 참조하고 있다면 공유 상태가 존재하는 것이다. 그리고 <Topic 34. 공유 상태는 틀린 상태>다.
    • 동시성을 갖춘 애플리케이션을 구축하는 더 나은 방법들이 있다. '액터(actor) 모델'이 그중 하나다.
    • 액터 모델에서는 프로세스들이 독립적으로 수행되며 서로 데이터를 공유하지 않는다. 대신 채널을 통해 잘 정의된 단순한 의미론을 사용하여 의사소통한다.
    • 동시에 혹은 병렬로 작동하는 코드가 신기하던 시절도 있었지만 이제는 필수이다.

    Topic 33 시간적 결합 깨트리기

    • '시간적 결합(temporal coupling)'이란 도대체 무엇인가?
    • 소프트웨어의 설계 요소로서 시간의 역할 중 중요한 두 가지 요소가 있다.
      • 동시성: 동시에 일어나는 일들
      • 순서: 시간의 흐름 속에서 일들의 상대적인 위치
    • 아키텍처를 설계하거나 프로그램을 짜기 시작할 때는 보통 직선적 사고를 하기 마련이다.
      • 사람들의 사고방식이 대개 그렇다. '이것을 하고, 그런 다음에 저것을 하고'
      • 하지만 이런 식으로 생각하다 보면 시간적 결합을 만들게 된다.
      • 메서드 A는 언제나 반드시 메서드 B보다 먼저 호출해야 한다.
    • 이러한 접근 방법은 그다지 유연하지 않고 현실과도 동떨어져 있다.
    • 우리는 동시성을 확보해야 한다.
    • 시간이나 순서에 의존하는 시간적 결합을 끊는 방법을 생각해 내야 한다. 그렇게 함으로써 유연성도 얻을 수 있고, 작업 흐름 분석과 아키텍처, 설계, 배포와 같은 개발의 여러 측면에서 시간과 관련된 의존성도 함께 줄일 수 있다.
    • 결과적으로 분석하기 더 쉽고 응답속도도 더 빠르며 더 안정적인 시스템을 만들 수 있을 것이다.

    동시성 찾기

    • '활동 다이어그램(activity diagram)' 같은 표기법을 사용해서 작업 흐름을 기록하는 것이 한 방법이다.

    Tip 56 작업 흐름 분석으로 동시성을 개선하라.

    • 활동 다이어그램을 사용하면 동시에 수행할 수 있는데도 아직 동시에 하고 있지 않은 활동들을 찾아내서 병렬성을 극대화할 수 있다.

    Topic 34 공유 상태는 틀린 상태

    • 레스토랑 예시 (책)

    비-원자적 갱신

    • 갱신하는 동작이 원자적(atomic)이지 않기 때문이다.
    • 실제 값이 그사이에 바뀔 수 있다.
    • 어떻게 원자적으로 바꿀 수 있을까?
    • 세마포어 및 다른 상호 배제 방법
      • 세마포어(semaphore)는 단순히 한 번에 한 사람만이 가질 수 있는 무언가다.
      • 세마포어를 만들어서 다른 리소스의 사용을 제어하는 데 쓸 수 있다.
      • 전통적으로는 세마포어를 획득하는 작업을 P로, 반환하는 작업을 V로 불렀지만, 요즈음은 lock/unlock, claim/release 등으로 부른다.
      • 이 접근 방식에는 몇 가지 문제가 있다.
        • 가장 큰 문제는 모든 프로세스가 세마포어를 사용해야만 제대로 동작한다는 것이다.
    • 리소스를 트랜잭션으로 관리하라
      • 현재의 설계가 미흡한 것은 사용하는 사람에게 책임을 전가하기 때문이다.
      • 제어를 중앙으로 집중시키자.
      • 그러려면 API를 바꿔서 하나의 호출로 리소스를 확인함과 동시에 가져가도록 만들어야 한다.
        • 리소스 관리 코드를 리소스 클래스 안으로 옮긴다.
        •  이 메서드 자체도 여러 개의 스레드에서 동시에 호출될 수 있으므로 여전히 세마포어로 보호해야 한다.
          • 오류 발생 시에도 세마포어를 unlock 해주어야 한다.

    여러 리소스와 트랜잭션

    • 여러 리소스 자체를 하나의 리소스로 보는 것이다.

    트랜잭션이 없는 갱신

    • 공유 메모리는 동시성 문제의 원인으로 많이 지목받는다. 하지만 사실 수정 가능한 리소스를 공유하는 애플리케이션 코드 어디에서나 동시성 문제가 발생할 수 있다.
    • 코드의 인스턴스 둘 이상이 파일, 데이터베이스, 외부 서비스 등 어떤 리소스에 동시에 접근할 수 있다면 잠재적인 문제를 안고 있는 것

    Tip 58 불규칙한 실패는 동시성 문제인 경우가 많다.

    그 밖의 독점적인 접근

    • 대부분의 언어에는 공유 리소스에 독점적으로 접근하는 것을 도와주는 라이브러리가 있다.
    • 상호 배제(mutual exclusion)를 의미하는 뮤텍스(mutex)라고 부르기도 하고, 모니터(monitor)나 세마포어라고 부르기도 한다.

    Topic 35 액터와 프로세스

    작가가 없다면 이야기는 쓰이지 않을 것이다.
    배우(actor)가 없다면 이야기는 생명을 얻지 못할 것이다.
    - 앤지-마리 델산테
    • 액터(actor)와 프로세스를 사용하면 흥미로운 방식으로 동시성을 구현할 수 있다.
    • 공유 메모리 접근을 동기화하느라 고생할 필요도 없다.
    • '액터'는 자신만의 비공개 지역 상태(state)를 가진 독립적인 가상 처리 장치(virtual processor)다.
    • '프로세스'는 본래 더 일반적인 가상 처리기로, 보통 운영 체제가 동시성을 지원하기 위하여 구현한다.
      • 이번 Topic에서는 프로세스를 마치 액터처럼 동작하도록 관례를 만들어 제한적으로만 사용

    액터는 언제나 동시성을 띤다

    • 액터의 정의에서 찾아볼 수 없는 것이 몇 가지 있다.
      • 액터를 관리하는 것이 없다.
      • 시스템이 저장하는 상태는 오직 메시지 그리고 각 액터의 지역 상태뿐이다. 메시지는 수신자가 읽는 것 외에는 확인할 방법이 없고, 지역 상태는 액터 바깥에서는 접근이 불가능하다.
      • 모든 메시지는 일방향이다. 답장이란 개념이 없다.
      • 각 메시지를 끝날 때까지 처리하고 중간에 다른 일을 하지 않는다.
    • 결과적으로 액터들은 아무것도 공유하지 않으면서 비동기적으로 동시에 실행된다.

    Tip 59 공유 상태 없는 동시성을 위하여 액터를 사용하라.

    간단한 액터

    • 액터 예시 (책)

    드러나지 않는 동시성

    • 액터 모델에서는 동시성을 다루는 코드를 쓸 필요가 없다.
    • 공유된 상태가 없기 때문이다.
    • 액터는 수신하는 메시지에 따라서 알아서 실행된다.

    얼랭이 장을 마련하다

    • 얼랭(Erlang) 언어와 런타임은 액터 구현의 좋은 사례다.
    • 얼랭은 액터를 '프로세스'라고 부르는데 일반적인 운영 체제의 프로세스와는 다르다.
      오히려 우리가 여기서 이야기하는 액터에 더 가깝다.
    • 얼랭 그리고 얼랭의 후손인 엘리서만 액터가 있는 것은 아니다.
      다른 대부분의 언어에도 액터 구현이 있다.
    • python 액터 라이브러리 : Pykka
    • 동시에 실행되는 작업을 구현할 때 액터를 사용하라.

    Topic 36 칠판

    • 형사들이 살인 사건을 조사해서 사건을 해결하기 위해 '칠판(blackboard)'을 어떻게 사용하는지 한번 생각해 보라.
    • 칠판 접근 방법의 몇 가지 주요 특징은 다음과 같다.
      • 형사들은 다른 형사의 존재를 알 필요가 없다.
      • 형사들은 여러 유형이고 사건을 해결하고 싶은 마음만 공통점이다.
      • 수사 과정에서 여러 형사들이 다양한 시간에 접근한다.
      • 칠판에는 제한 없이 어떤 것이든 올릴 수 있다.
    • 일종의 '자유방임주의'적 동시성이다.
    • 각 형사는 독립된 프로세스, 에이전트, 액터 등과 같다.
    • 컴퓨터 기반의 칠판 시스템은 원래 음성 인식, 지식 기반 추론 시스템 등 해결해야 할 문제의 규모가 크고 복잡한 인공 지능 애플리케이션에서 사용되었다.

    칠판 사용 사례

    • 주택 담보 대출이나 신용 대출 신청을 받아서 처리하는 프로그램을 작성한다고 가정해 보자.
    • 정부, 금융위원회, 지방 자치 단체 각각 복잡한 규칙을 가지고 있다.
    • 추가적으로 많은 문제들이 있다.
      • 응답은 정해진 순서가 없이 도착한다.
      • 데이터가 비동기적으로 도착한다.
      • 어떤 데이터는 다른 데이터에 의존적이다.
      • 새로운 데이터는 새로운 정책을 적용해야 할 수도 있다.
    • 작업 흐름 시스템을 이용해서 모든 조합과 환경을 통제할 수도 있다.
      • 복잡하고 프로그래머의 노력이 많이 들어간다.
    • 칠판 시스템을 법적 요구 사항을 캡슐화하는 규칙 엔진과 함께 사용하면 이러한 어려움을 우아하게 해결할 수 있다.
      데이터의 도착 순서는 이제 상관없다.
    • 어떤 사실이 칠판에 올라가면 적절한 규칙이 발동되도록 하면 된다.
    • 결과에 대한 피드백도 마찬가지이다.
      어떤 규칙에서 나온 것이든 그 결과를 다시 칠판에 올려서 다른 규칙들이 발동 되록하면 된다.

    Tip 60 칠판으로 작업 흐름을 조율하라.

    메시지 시스템과 칠판의 유사성

    • 2019년 기준으로, 메시징 시스템(messaging system)으로 통신하는 작고 분리된 서비스들(MSA)로 애플리케이션을 구성하는 경우가 많다.
    • 카프카(Kafka)나 NATS 같은 메시징 시스템은 단순히 데이터를 A에서 B로 보내는 것보다 훨씬 많은 일을 한다.
    • 메시징 시스템을 칠판 또는 여러 액터를 실행하는 플랫폼으로 사용할 수 있다.

    하지만 그렇게 간단하지 않다...

    • 아키텍처에서 액터와 칠판, 마이크로서비스를 활용하면 애플리케이션에서 생길 수 있는 모든 종류의 동시성 문제를 예방할 수 있을 것이다.
      하지만 거기에는 비용이 따른다.
    • 이런 접근 방식을 사용하면 많은 동작이 간접적으로 일어나므로 분석이 더 힘들다.
    • 메시지 형식 및 API를 모아두는 중앙 저장소를 운영하면 도움이 될 것이다.
    • 고유한 추적 아이디(trace id)를 만들어서 사용하자
      • trace id를 해당 작업에 관여하는 모든 액터로 전파하면 로그 파일을 뒤져서 어떤 일이 일어났는지 재구성해 볼 수 있을 것이다.
    • 이런 종류의 시스템은 맞춰야 하는 구성 요소 수가 더 많기 때문에 배포하고 관리하기 더 까다롭다.
      하지만 그 결과 시스템이 더 잘게 쪼개지고, 전체 시스템이 아니라 개별 액터만 교체하여 시스템을 업데이트할 수 있다는 면에서 어느 정도 보상받는다.

     

     

    반응형

    댓글

Designed by Tistory.