[ Clean Code ] 클린코드 시리즈 - 동시성

2024. 2. 2. 05:37Computer Science

2024년 1월 28일, 책 클린코드를 함께 읽는 스터디가 종료되었다. 자바로 쓰여졌지만, 자바로 공부하시는 분들이 계셔서 이해안되는 부분은 링크 참조나 설명해주셔서 도움이 많이 되었다. 챕터는 15. JUnit 까지 마쳤지만, 여전히 읽을 챕터와 부록은 남아 있다. 틈틈이 시간 내서 읽어보도록 해야지..

 

각설하고, 클린코드 동시성 타이틀을 발견하고, 나는 드디어 올 것이 왔구나 싶었다. 

예전에 Swift 를 처음 배울 때 이미지를 Api 에서 요청해 가지고 오고, reloadData로 띄워 프로젝트에서 적용하는 부분에서 DispatchQueue.main.async 를 배웠던 적이 있었는데, 이게 무슨 말인가? 그냥 쓰는 거구나 하고 썼다.. 

내적으로는 '디스패치 큐! 이게 대체 뭐길래 쓰는건가?? 알고 싶다,,' 였지만,,써야만 했던 교육과정이었기에..그냥 썼다..😂 

그때 당시 비동기에 관해 공부하면서 동시성에 대해 강사님이 언급해주셨다.. 그래서 동시성이 뭔가 하고 흐르듯 한 번 보고  

지나갔는데, 반가웠다. 간간이 동시성, 동시성 하며 사람들은 언급했기에..

운영체제 상, Dispatch(디스패치) 
시스템에서 관리하는 디스패치 대기열에 작업(task)를 제출해 멀티코어 하드웨어에서 동시에 코드를 실행할 수 있다. 


비동기, 동시성,, 다 앱의 성능과도 연관이 있는 키워드다. 하지만 정말 성능을 높여주기만 할까? 클린코드에서도 언급했듯, 동시성과 깨끗한 코드가 공존하기란 힘들다고 했다. 앱의 성능을 높이기 위해서는 여러 스레드를 동시에 돌리는 것인데, 동시에 돌린다면 발생하는 문제점들도 있을 것이다.

 

그래서 동시성이란 무엇일까?

동시성을 알려면 컴퓨터 운영체제에 대해서도 간략하게 알아야한다. 

프로세서: 하드웨어 측면에서 컴퓨터 내 프로그램을 수행하는 유닛, 대표적으로 중앙처리장치(CPU) 가 있다. 
• 코어: 주요 연산회로
    -> 싱글코어, 멀티코어,...,옥타코어 
• 프로그램: 보조기억 장치에 저장된 실행 코드, 
• 프로세스: 프로그램을 구동해 프로그램 자체와 프로그램 상태가 메모리상에서 실행되는 작업 단위
스레드: 하나의 프로세스 내에서 실행되는 작업 흐름 단위
    -> 프로세스 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있음
처리량: 애플리케이션이 제한된 시간에 처리할 수 있는 양, 트랜잭션 수, 시간당 처리 가능한 페이지 수

 

동시성(Concurrency)은 Task(작업)들이 빠르게 전환하면서 실행되어 동시에 실행되는 것처럼 보이는 것. 싱글코어(멀티 코어에서도 가능)에서 멀티스레드를 동작시키기 위한 방식으로 멀티 태스킹을 위해 여러 개의 스레드가 번갈아 가면서 실행되는 방식이다. 

동시성을 이용한 싱글 코어 멀티 태스킹은 각 스레드들이 병렬적으로 실행되는 것처럼 보이지만 서로 번갈아 가면서 실행되고 있는 방식이다.

 

동시성을 왜 사용할까? 

처리량Throughput이 향상된다. 한 유저의 요청을 처리하는 데 1초가 필요. 유저가 늘어나는 경우 모든 유저는 자신보다 먼저 도착한 요청이 끝날 때까지 기다려야한다. 이런 경우 동시성이 여러 유저를 동시에 처리함으로써 처리량을 향상시킬 수 있다. 

또한, 무엇을 할 것인가? 언제 끝날 것인가 간의 의존성을 해소한다. 

 

동시성은 항상 옳은 걸까?

항상 퍼포먼스를 향상 시키는 걸까? 동시성은 앞에서 밥 아저씨가 우려한 바와 같이 시스템이 부하를 받기 전까지는 돌아가지만, 그말은 부하를 유발한다는 거다. 동시성은 복잡해서 버그 재현도 어렵고, 구현하려면 설계 전략을 제대로 짜야한다.

또한,  전역 자원의 공유가 어렵다. 동시에 실행되는 작업들인데 순차적으로 진행된다기보단 여러 작업이 동시 다발적으로 실행되기 때문이지 않을까 싶다. 그래서 프로그래밍 오류를 찾아내는 것도 쉽지 않을 것이다. 

 

 

동시성으로 성능적인 측면에서 처리량을 늘리고 코드도 개선될 것 같지만 실질적으로는 복잡해질 위험이 도사릴 수 있다..!!😂

 

동시성을 보완해 낼 방법들이 있다. - 동시성 방어 원칙

단일 책임의 원칙(Single Responsibility Principle) 

이 원칙은 메서드 / 클래스 / 컴포넌트가 변경될 이유는 하나인 것이다. 즉, 다시 말해 동시성 관련 코드는 복잡성이 높아질 수 있음으로 그 기능만 하도록 다른 코드들과 분리되어야 한다는 것이다.

 

자료 범위를 제한하자.

공유 객체를 사용하는 코드 내 임계영역(Critical Section) 을 synchronized 키워드로 보호하라고 권장한다. 

자료를 캡슐화(encapsulation) 하라. 공유 자료를 최대한 줄이는 것.

 

임계영역(Critical Section)
공유자원의 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역 / 둘 이상의 프로세스가 동시접근 해서는 안되는 공유자원에 접근하는 코드의 일부다.

임계영역의 수 줄이는 기술 중요 → 공유자료를 수정하는 위치가 많다
- 보호할 임계영역 빼먹음
- 그렇지 않아도 찾아내기 힘든 버그 더 찾기 어려워짐

자료 사본 사용하라

공유자료 줄이는 방법 → 처음부터 공유하지 않음

1객체를 복사해 읽기 전용으로 사용하는 방법

스레드는 가능한 독립적으로 구현

다른 스레드와 자료를 공유하지 않는다. 각 스레드는 클라이언트 요청 하나를 처리한다.

 

라이브러리를 이해하라

  • 자바에서 제공하는 thread safe 컬렉션을 사용
  • 연관 없는 태스크 수행시 executor 프레임워크 사용
  • 가능하다면 non blocking 사용
  • 일부 클래스 라이브러리는 스레드에 안전하지 못하다
🛡️Blocking / Non-Blocking
다른 요청 작업 처리하기 위해 현재 작업을 block(차단, 대기) 하냐 안하냐의 유무
→ 흐름 자체를 막냐 안 막냐
blocking: 즉시 리턴하지 않음 (일을 못하게 막음)
Non-Blocking: 즉시 리턴

 

실행모델 이해하기

다중 스레드 애플리케이션 분류

  1. 한정된 자원(bound resources): 다중 스레드 환경에서 사용하는 자원으로 크키나 숫자가 제한적 e.g. 데이터베이스 연결, 고정된 크기의 읽기 쓰기 버퍼 등
  2. 상호배제: 한 번에 한 스레드만 공유 자료나 공유 자원을 사용할 수 있는 경우를 가리킨다.
  3. 기아(Starvation): 한 스레드나 여러 스레드가 굉장히 오랫동안 혹은 영원히 자원을 기다림. e.g. 항상 짧은 스레드에게 우선순위를 준다면, 짧은 스레드가 지속적으로 이어질 경우, 긴 스레드가 기아 상태에 빠짐.
  4. 데드락(교착상태): 여러 스레드가 서로 끝나기를 기다린다. 모든 스레드가 각기 필요한 자원을 다른 스레드가 점유하는 바람에 어느 쪽도 더이상 진행하지 못함
  5. 라이브락
  • 생산자 - 소비자
  • 읽기 - 쓰기
  • 식사하는 철학자들 은유

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

권장사항: 공유객체 하나에는 메서드 하나만 사용

  • 클라이언트 잠금
  • 서버 잠금
  • 연결 서버

동기화하는 부분 작게 만들기

Synchronized 로 수행되는 잠금은 딜레이와 오버헤드를 만들기 때문에 비싼 수행으로 간주되면 가능한 작게 만든다. 반면 임계영역은 꼭 보호 되어야한다.

올바른 종료코드 구현 어려움

스레드 코드 테스트

권장사항: 문제 노출하는 테스트케이스를 짜라

프로그램 설정, 부하 돌려가며 테스트 실패시 원인 추적하라.

말이 안되는 실패는 잠정적인 스레드 문제 취급하라

권장: 시스템실패는 일회성이 아니다.

다중 스레드 고려하지 않은 순차코드 부터 제대로 돌게하자

다중스레드 관련은 238p.

자동화

결론

Keywords

SRP 준수, 자바 POJO, 테스트 용이성 TDD

동시성 오류를 일으키는 잠정적인 원인 찾아 제거

보호할 코드 영역

특정코드 영역 잠구는 것

공유하는 객체 수와 범위를 최대한 줄인다

 

모든 언어에서 동시성은 중요하겠지만, 적절하게 필요할 때 쓰는 것이 중요하다. 그리고 처리량을 관리해주는 동시성을 현명하게 분리해야만 깨끗한 코드를 유지할 수 있다는 조언이었고, 그 조언의 방법은 익숙해보이지만 실행해 본적은 없는 것들이었다. 이 책의 주요 프로그래밍 언어였던 자바뿐만 아니라,  Swift 에서도 동시성은 자주 언급이 된다. 애플은 똑똑하게 동시성을 관리해주는 시스템을 제공해주고, 개발자에게  thread safe 하지 않아지는 부분에 대해서는 설정을 할 수 있도록 해주었다. 

그 부분에 대해선 iOS 카테고리의 다른 글에서 언급을 해봐야겠다. 

 

참고 

도서 클린코드(Clean Code - Uncle Bob) - Chapter 13. Concurrency

동시성 프로그래밍