[Book]Clean Code 13장

동시성이 필요한 이유?

  • 동시성은 결합을 없애는 전략이다. 무엇과 언제를 분리하는 전략이다.
  • 스레드가 하나인 프로그램은 무엇과 언제가 서로 밀접하다.
  • 무엇과 언제를 분리하면 애플리케이션 구조와 효율이 극적으로 나아진다.

동시성의 오해

  • 동시성은 항상 성능을 높여준다 → 때로 성능을 높여준다. 대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있거나, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다.
  • 동시성을 구현해도 설계는 변하지 않는다. → 단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다. 일반적으로 무엇과 언제를 분리하면 시스템 구조가 크게 달라진다.
  • 웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다. → 실제로는 컨테이너가 어떻게 동작하는지, 어떻게 동시 수정, 데드락 등과 같은 문제를 피할 수 있는지를 알아야만 한다.

동시성의 타당한 생각

  • 동시성은 다소 부하를 유발한다. → 성능 측면에서 부하가 걸리며 코드도 더 짜야한다.
  • 동시성은 복잡하다 → 간단한 문제라도 동시성은 복잡하다.
  • 일반적으로 동시성 버그는 재현하기 어렵다 → 그래서 진짜 결함이 아닌 일회성 문제로 여겨 무시하기 쉽다
  • 동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야 한다.

동시성 방어 원칙

  • 단일 책임 원칙 : SRP는 메서드/클래스/컴포넌트를 변경할 이유가 하나여야 한다는 원칙이다. 동시성은 복잡성 하나만으로도 따로 분리할 이유가 충분하다. 즉, 동시성 관련 코드는 다른 코드와 분리해야 한다는 뜻이다.
  • 따름 정리 - 자료 범위를 제한하라 : 임계영역을 키워드로 보호하라. 자료를 캡슐화하고 공유자료를 최대한 줄여라.
  • 따름 정리 - 자료 사본을 사용하라 : 어떤 경우에는 객체를 복사해 읽기 전용으로 사용하는 방법이 가능하다. 어떤 경우에는 각 스레드가 객체를 복사해 사용한 후 한 스레드가 해당 사본에서 결과를 가져오는 방법도 가능하다.
  • 따름 정리 - 스레드는 가능한 독립적으로 구현하라 : 다른 스레드와 자료를 공유하지 않는 스레드를 구현한다. 각 스레드는 클라이언트 요청 하나를 처리한다.

실행 모델을 이해하라

  • 한정된 자원 : 다중 스레드 환경에서 사용하는 자원으로, 크기나 숫자가 제한적이다. 데이터베이스 연결, 길이가 일정한 읽기/쓰기 버퍼 등
  • 상호 배제 : 한번에 한 스레드만 공유 자료나 공유 자원을 사용할 수 있는 경우를 가리킨다.
  • 기아 : 한 스레드나 여러 스레드가 굉장히 오랫동안 자원을 기다린다.
  • 데드락 : 여러 스레드가 서로가 끝나기를 기다린다. 모든 스레드가 각기 필요한 자원을 다른 스레드가 점유하는 바람에 어느 쪽도 더 이상 진행하지 못한다.
  • 라이브락 : 락을 거는 단계에서 각 스레드가 서로를 방해한다. 스레드는 계속해서 진행하려 하지만 공명으로 인해 오랫동안 진행하지 못한다.

동기화하는 메서드 사이에 존재하는 의존성을 이해하라

  • 공유 객체 하나에는 메서드 하나만 사용하라.
  • 공유 객체 하나에 여러 메서드가 필요한 상황의 경우 다음 3가지 방법을 고려한다.
    1. 클라이언트에서 잠금 - 클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다. 마지막 메서드를 호출할 때까지 잠금을 유지한다.
    2. 서버에서 잠금 - 서버에 “서버를 잠그고 모든 메서드를 호출한 후 잠금을 해제”하는 메서드를 구현한다. 클라이언트는 이 메서드를 호출한다.
    3. 연결 서버 - 잠금을 수행하는 중간 단계를 생성한다.

동기화하는 부분을 작게 만들어라

  • 임계영역 수를 최대한 줄여야 한다.
  • 임계영역 수를 줄인다고 거대한 임계영역 하나로 구현하지 마라. 필요 이상으로 임계영역을 키우면 스레드 간에 경쟁이 늘어나고 프로그램의 성능이 떨어진다.

올바른 종료 코드는 구현하기 어렵다

  • 영구적으로 돌아가는 시스템을 구현하는 방법과 잠시 돌다 깔끔하게 종료하는 시스템을 구현하는 방법은 다르다. 깔끔하게 종료하는 다중 스레드 코드를 짜야 한다면 시간을 투자해 올바르게 구현하자
  • 종료 코드를 개발 초기부터 고민하고 동작하게 초기부터 구현하라. 생각보다 어려우므로 이미 나온 알고리즘을 검토하라.

스레드 코드 테스트하기

  • 문제를 노출하는 테스트 케이스를 작성하라
  • 프로그램 설정과 시스템 설정과 부하를 바꿔가며 자주 돌려라.
  • 테스트가 실패하면 원인을 추적하라. 다시 돌렸더니 통과하더라라는 이유로 넘어가면 절대로 안 된다.
  • 말이 안되는 실패는 잠정적인 스레드 문제로 취급하라
  • 다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자
  • 다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있도록 스레드 코드를 구현하라
  • 다중 스레드를 쓰는 코드 부분을 상황에 맞춰 조정할 수 있게 작성하라
  • 프로세서 수 보다 많은 스레드를 돌려보라
  • 다른 플랫폼에서 돌려보라
  • 코드에 보조코드를 넣어 돌려라. 강제로 실패를 일으키라.

카테고리:

업데이트:

댓글남기기