객체는 처리의 추상화다. 스레드는 일정의 추상화다.
동시성과 깔끔한 코드는 양립하기 아주 어렵다
동시성이 필요한 이유
- 동시성은 결합(Coupling)을 없애는 전략이다.
- 무엇과 언제를 분리하는 전략이다.
- 응답 시간과 처리량 개선
- 많은 사용자를 동시에 처리하면 시스템 응답 시간을 높일 수 있다.
- 대량의 정보 병렬 처리
동시성 방어 원칙
단일 책임 원칙 Single Responsibility Principle, SRP
- SRP는 주어진 메서드/클래스/컴포넌트를 변경할 이유가 하나여야 한다는 원칙이다.
- 동시성은 복잡성 하나만으로도 따로 분리할 이유가 충분하다. 즉, 동시성 관련 코드는 다른 코드와 분리해야 한다.
자료 범위를 제한하라
- 객체 하나를 공유한 후 동일 필드를 수정하던 두 스레드가 서로 간섭하므로 예상치 못한 결과를 내놓는다.
- 공유 객체를 사용하는 코드 내 임계영역을 synchronized 키워드로 보호하라고 권장한다.
- 자료를 캡슐화 하고 공유 자료를 최대한 줄여라
자료 사본을 사용하라
- 공유 자료를 줄이려면 처음부터 공유하지 않는 방법이 제일 좋다.
- 어떤 경우에는 객체를 복사해 읽기 전용으로 사용하는 방법이 가능하다.
- 어떤 경우에는 각 스레드가 객체를 복사해 사용한 후 한 스레드가 해당 사본에서 결과를 가져오는 방법도 가능하다.
스레드는 가능한 독립적으로 구현하라
- 다른 스레드와 자료를 공유하지 않는다.
- 각 스레드는 클라이언트 요청 하나를 처리한다.
- 모든 정보는 비공유 출처에서 가져오며 로컬 변수에 저장한다.
- 독자적인 스레드로, 가능하면 다른 프로세서에서, 돌려도 괜찮도록 자료를 독립적인 단위로 분할하라.
라이브러리를 이해하라
실행 모델을 이해하라
생산자-소비자 Producer-Consumer
읽기-쓰기 Readers-Writers
식사하는 철학자들 Dining Philosophers
동기화하는 메서드 사이에 존재하는 의존성을 이해하라
- 동기화하는 메서드 사이에 의존성이 존재하면 동시성 코드에 찾아내기 어려운 버그가 생긴다.
- 공유 클래스 하나에 동기화된 메서드가 여럿이라면 구현이 올바른지 확인하자
- 공유 객체 하나에는 메서드 하나만 사용하라
- 공유 객체 하나에 여러 메서드가 필요한 상황인 경우
- 클라이언트에서 잠금: 클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다. 마지막 메서드를 호출할 때까지 잠금을 유지한다.
- 서버에서 잠금: 서버에다 "서버를 잠그고 모든 메서드를 호출한 후 잠금을 해제하는" 메서드를 구현한다. 클라이언트에서 호출
- 연결 서버: 잠금을 수행하는 중간 단계를 생성한다. '서버에서 잠금' 방식과 유사하지만 원래 서버는 변경하지 않는다.
동기화하는 부분을 작게 만들어라
- 여기저기서 synchronized 문을 남발하는 코드는 바람직하지 않다.
- 반면, 임계영역은 반드시 보호해야 한다. 임계영역 수를 최대한 줄이자.
- 필요 이상으로 임계영역 크기를 키우면 스레드 간에 경쟁이 늘어나고 프로그램 성능이 떨어진다.
올바른 종료 코드는 구현하기 어렵다
- 영구적으로 돌아가는 시스템을 구현하는 방법과 잠시 돌다 깔끔하게 종료하는 시스템을 구현하는 방법은 다르다.
- 깔끔하게 종료하는 코드는 올바로 구현하기 어렵다.
가장 흔히 발생하는 문제 데드락
- 생각보다 어려우므로 이미 나온 알고리즘을 검토하자
스레드 코드 테스트하기
- 코드가 올바르다고 증명하기는 현실적으로 불가능하다. 테스트가 정확성을 보장하지는 않지만 충분한 테스트는 위험을 낮춘다.
- 말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라.
'일회성'이라 치부하지 마라
- 다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자.
- 다중 스레드를 쓰는 코드 부분을 다양한 설정으로 실행하기 쉽게 구현하라
- 다중 스레드를 쓰는 코드 부분을 상황에 맞게 조율할 수 있게 작성하라
- 프로세서 수보다 많은 스레드를 돌려보라
스와핑이 잦을수록 임계영역을 빼먹은 코드나 데드락을 일으키는 코드를 찾기 쉬워진다
- 다른 플랫폼에서 돌려보라
- 코드에 보조 코드instrument를 넣어 돌려라. 강제로 실패를 일으키게 해보라
- 자동화
보조 코드를 자동으로 추가하는 도구를 사용한다.
흔들기 기법을 사용해 오류를 찾아내자
결론
다중 스레드 코드는 올바로 구현하기 어렵다.
각별히 깨끗하게 코드를 짜야 한다.