선생님, 개발을 잘하고 싶어요.

자바 병렬 프로그래밍 - 2부 9장 - GUI 애플리케이션 본문

일상/책 리뷰

자바 병렬 프로그래밍 - 2부 9장 - GUI 애플리케이션

알고싶은 승민 2022. 6. 25. 09:59
  • 모든 GUI 툴킷은 GUI 관련 작업이 모두 단일 스레드에서 일어나는 단일 스레드 서브시스템으로 구현돼 있다.
  • 단일 스레드 서브시스템 👉 말 그대로, 하나의 스레드 위에서 동작하는 시스템
  • 현실적으로 프로그램이 하나의 스레드만 사용하며 동작하는 건 말이 안되고, 작업 중 일부는 이벤트 스레드, 일부는 일반 애플리케이션 스레드에서 실행된다.
  • 🤔 GUI를 그리는 안드로이드도 이러한 제약이 있다. 안드로이드에서 메인 스레드는 GUI에 접근하고 수정할 수 있는 유일한 스레드를 말하지만, 이 책에서 말하는 그런 역할을 하는 스레드는 이벤트 스레드다.

GUI는 왜 단일 스레드로 동작하는가?

  • 터치 이벤트 등 OS → App 방향 수정, 컴포넌트 배경색 설정 등 App → OS 방향 수정으로 하나의 GUI 컴포넌트를 두고 양방향으로 움직이는 과정이 필연적이다.
  • 이 과정에서 멀티 스레드 사용 시 경쟁 조건과 데드락 등의 문제가 계속 발생
  • 그런 프레임워크를 만든다고 해도, 이를 기반으로 작업하려면 애플리케이션 개발자가 프레임워크 내부의 동기화 정책에 대해서 매우 잘 이해하고 섬세하게 코드를 작성해야 한다. 👎
  • 단일 스레드 GUI 프레임워크는 스레드 제한 기법으로 스레드 안전성을 보장한다.
  • 🌟 이러한 이유로 대부분의 GUI 프레임워크가 이벤트 처리용 전담 스레드를 만들고, 전담 스레드는 큐에 쌓여 있는 이벤트를 가져와 애플리케이션에 준비돼 있는 이벤트 처리 메소드를 호출해 기능을 동작시키는 단일 스레드 이벤트 큐 모델을 정착했다.

순차적 이벤트 처리

  • 마우스 클릭, 키보드 입력, 타임 아웃 👉 이벤트 👉 일종의 작업(task)
  • 이러한 이벤트는 항상 순차적으로 실행된다.
  • 응답성 문제로 인해 이벤트 스레드에서 실행되는 작업은 작업을 빨리 끝내야한다.

스윙의 스레드 한정 (🤔 안드로이드도 비슷할 듯)

  • GUI 객체는 스레드 한정 기법을 사용해 스레드 안전성 확보
  • 스윙 이벤트 스레드는 이벤트 큐에 쌓여 있는 작업을 순차적으로 처리하는 단일 스레드 Executor로 볼 수 있다.

짧게 실행되는 GUI 작업

  • 프레임워크는 발생하는 이벤트를 애플리케이션 전달 (프레임워크 ➡️ 앱)
  • 애플리케이션은 이벤트 대응 코드를 통해서 프레임워크에 포함된 내용 변경한다. (앱 ➡️ 프레임워크)
  • 🌟 짧으니까 응답성 신경 쓸 일도 없다, 그냥 이벤트 스레드에서 실행 가능

장시간 실행되는 GUI 작업

  • GUI 애플리케이션에선 시간이 오래 걸릴 작업을 기계적으로 대량 생성하는 일은 거의 없기 때문에 스레드가 무한정 늘어나는 가능성이 거의 없기 때문에 👉 newCachedThreadPool로 생성한 Executor가 제격이다.

작업 중단

  • 사용자가 취소하고 싶을 수 있음, 화면 라이프사이클에 의해 작업이 멈춰야 할 수 있음
  • 중단 가능한 작업을 만들 수 있도록 설계된 API를 사용하자. (JDK - Future, Rx - Disposable, Coroutine - Job)

진행 상태 및 완료 알림

  • 작업 완료를 알리는 API 사용하자.

작업 처리에서 중요한 내용

  • 작업 중단, 작업 완료 알림, 작업 진행 상태 알림 …

데이터 공유 모델

스레드 안전 데이터 모델 (구현 복잡)

  • 특정 데이터를 두고 여러 스레드가 동시 동작하는 상황은 스레드 안전 데이터 모델을 사용해 쉽게 해결
  • 병렬 환경에서 원활하게 동작하고 오래된 버전의 데이터를 적절한 시점에 자동으로 제거하는 등 기능 갖추는 건 그다지 쉬운 일이 아니다. (🤔 Reactive Programming 진영이 해결하는 문제?)

분할 데이터 모델 (구현 쉬움)

  • 분할 데이터 모델 👉 화면 표시 부분(presentation domain), 애플리케이션 부분(application domain)의 데이터 모델을 구분해 사용하는 모델
  • 화면 표시 모델은 공유 모델에 이벤트 리스너 등록으로 최신 내용 반영 가능
    • 스냅샷(객체)을 받아서 처리
      • 👉 데이터 규모 작고, 변경 빈도가 높지 않고, 공유 모델과 화면 표시 모델 데이터 구조가 비슷해야함. (👀 스냅샷을 복사해서 전달하니까 당연히 그러하겠다)
    • 변경된 부분만 추려서 보내는 증분 업데이트(incremental update) 방법이 효율적 (🤔 안드로이드도 이러한 방법론을 활용한 케이스가 있나?)
    • 변경 이벤트를 받고 직접 데이터 접근해 처리

다른 형태의 단일 스레드 서브시스템

  • 스레드 제한 기법은 GUI만의 전유물이 아님
  • GUI에서 다룬 기법들이 다른 단일 스레드 서브시스템 활용할 때도 유용함
    • 해당 라이브러리를 독점 사용하는 전용 스레드나 단일 스레드 Executor 준비
    • 라이브러리에 접근하는 이벤트를 전용 스레드에 등록하는 프록시 활용

요약

  • 🌟 장시간 실행 될 작업은 이벤트 스레드가 아닌 백그라운드 스레드에서 실행시켜야 한다.
Comments