본문 바로가기
책/Clean Code

[Clean Code] 13장 동시성

by 오오오니 2025. 3. 17.

13 동시성

2024년 3월 18일 오후 3:29
비어 있음

✏️ 동시성이 필요한 이유

동시성은 결합을 없애는 전략
동시성 구현이 필요한 상황
매일 수많은 웹 사이드에서 정보를 가져와 요약하는 정보 수집기가 단일 스레드 프로그램이라면 24시간 안에 정보를 수집할 수 없다. 대신 다중 스레드 알고리즘을 이용하면 수집기 성능을 높일 수 있다.
동시성에 관한 오해
동시성은 항상 성능을 높여준다.
⇒ 때로 성능을 높여준다. 여러 프로세서가 동시에 처리할 독립적인 계산이 충분이 많은 경우
동시성을 구현해도 설계는 변하지 않는다.
⇒ 무엇과 언제를 분리하면 시스템 구조가 달라진다.
웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다.
⇒컨테이너 동작 원리를 알아야하고 동시 수정, 데드락 같은 문제를 피할 수 있는지알아아야한다.
동시성에 관해 타당한 생각
동시성은 다소 부하를 유발한다.
동시성은 복잡하다
일반적으로 동시성 버그는 재현하기 어렵다.
동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야한다.

✏️ 난관

두 스레드가 자바 코드 한 줄을 거쳐가는 경로는 수업이 많은데 잘못된 결과를 내놓는 것은 일부 경로이다.
예제 코드에서 경로는 최대 12,870 개

✏️ 동시성 방어 원칙

동시성 코드가 일으키는 문제로부터 시스템을 방어하는 원칙과 기술들
단일 책임 원칙
동시성은 복잡성 하나만으로도 따로 분리할 이유가 충분하다. 동시성관 관련된 코드를 분리하라
따름 정리
자료 범위를 제한하라
공유 객체를 사용하는 코드 내 임계영역 synchronized 키워드로 보호하라
자료 사본을 사용하라
스레드는 가능한 독립적으로 구현하라
다른 스레드와 자료를 공유하지 않기. 하지만 결국 데이터베이스 연결과 같은 자원을 공유하는 상황에 처한다.

✏️ 라이브러리를 이해하라

자바 5는 동시성 측면에서 이전 버전보다 나아졌다.
스레드 환경에 안전한 컬렉션
java.util.concurrent 패키지에 있다. HashMap - ConcurrentHashMap
서로 무관한 작업을 수행할 떄는 executor 프레임워크 사용
ReentrantLock, Semaphore, CountDownLatch 같은 추가된 클래스들이 좀 더 복잡한 동시성 설계를 지원한다.

✏️ 실행 모델을 이해하라

기본 용어
한정된 자원 (Bound Resource)
다중 스레드 환경에서 사용하는 자원으로, 크기나 숫자가 제한적이다. 데이터베이션 연결, 길이가 일정한 읽기/쓰기 버퍼가 예다
상호 배제 (Mutual Exclusion)
한 번에 한 스레드만 공유 자료나 공유 자원을 사용할 수 있는 경우를 가리킨다.
기아 (Starvation)
한 스레드나 여러 스레드가 굉장히 오랫동안 혹은 영원히 자원을 기다린다. 예를 들어, 항상 짧은 스레드에게 우선순위를 준다면, 짧은 스레드가 지속적으로 이어질 경우, 긴 스레드가 기아 상태에 빠진다.
데드락 (Deadlock)
여러 스레드가 서로가 끝나기를 기다린다. 모든 스레드가 각기 필요한 자원을 다른 스레드가 점유하는 바람에 어느 쪽도 더 이상 진행하지 못한다.
라이브락 (Livelock)
락을 거는 단계에서 각 스레드가 서로를 방해한다. 스레드는 계속해서 진행하려 하지만, 공명으로 인해, 굉장히 오랫동안 혹은 영원히 진행하지 못한다.
다중 스레드 프로그래밍에서 사용하는 실행 모델
생상자 - 소비자
생상자 스레드가 정보를 생성해 버퍼나 대기열에 넣는다.
소비자 스레드가 대기열에서 정보를 가져와 사용한다.
대기열은 한정된 자원이다.
문제 : 시그널을 통해 진행하는데 서로 시그널을 기다릴 가능성이 존재
읽기 - 쓰기
읽기 스레드가 공유자원을 사용하고 쓰기 스레드가 가끔 갱신하는 상황에서 처리율이 문제이다. 읽기 스레드의 요구와 쓰기 스레드의 요구를 적절히 만족시켜 처리율도 높이고 기아도 방지하는 해법이 필요하다.
식사하는 철학자들
철학자가 양손으로 포크를 집어야 밥을 먹을 수 있다. 철학자가 스레드, 포크가 자원일 때 여러 프로세스가 자원을 얻으려 경쟁한다. 주의해서 설계하지 않으면 데드락, 라이브락, 처리율 저하, 효울성 저하 등을 겪는다.

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

동기화하는 메스드 사이에 의존성이 존재하면 동시성 코드에 찾아내기 어려운 버그가 생긴다.

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

synchronized 키워드를 사용하면 락을 설정할 수 있지만 락의 범위는 한 스레드만 실행이 가능하다. 락은 스레드를 지연시키고 부하를 가중시키므로 이 키워드를 남발하지 말고 동기화하는 부분을 최대한 작게 만들어라

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

데드락같은 경우가 있어 깔끔하게 종료하는 코드는 올바로 구현하기 어렵다.
깔끔하게 종료하는 다중 스레드 코드를 짜야 한다면 시간을 투자해 올바로 구현하자
이미 나온 알고리즘을 검토하라

✏️ 스레드 코드 테스트하기

테스트가 위험을 낮추게 해준다.
문제를 노출하는 테스트 케이스를 작성하라. 프로그램 설정과 시스템 설정, 부하를 바꿔가며 자주돌려라
몇 가지 구체적인 지침
말도 안되는 실패는 잠정적인 스레드 문제로 취급하라
스레드 코드에 잠입한 버그는 아주 수백만 번에 한번씩 들어나기도 한다. 일회성 문제로 치부하지 마라
다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자.
다중 스레드를 쓰는 코드 부분을 상황에 맞춰 조정할 수 있게 작성하라
적절한 스레드 개수를 파악하는 것은 상당한 시행착오가 필요하다.
프로세서 수보다 많은 스레드를 돌려보라
데드락을 일으키는 코드를 찾기 쉬워진다.
다른 플랫폼에서 돌려보라
코드에 보조코드를 넣어 돌려라 강제로 실패를 일으키게 해보라
AOF 와 같은 도구를 사용하여 자동화 할 수 있다.

✏️ 결론

다중 스레드 코드는 올바로 구현하기 어렵다. 주의해서 작성하지 않으면 오묘한 오류에 직면한다. SRP 준서, 동시성 오류의 잠정적인 원인 이해, 라이브러리와 알고리즘 이해가 필요하다. 어떻게든 문제는 생긴다. 일화성을 치부하지 말고 반복적으로 테스트하자
리스트 보기

' > Clean Code' 카테고리의 다른 글

[Clean Code] 17장 냄새와 휴리스틱  (1) 2025.03.17
[Clean Code] 15장 JUnit 들여다 보기  (0) 2025.03.17
[Clean Code] 11장 시스템  (0) 2025.03.17
[Clean Code] 9장 단위 테스트  (0) 2025.03.17
[Clean Code] 7 장 오류 처리  (0) 2025.03.17