[운영체제] ThreadPool, Monitor, Fork-Join

지니🧸·2023년 4월 7일
0

CS 저장소

목록 보기
13/48

🌊 Thread Pool

Thread Pool

개념

  • 병렬 작업 처리 증가는 스레드 개수 증가로 직결.
  • 스레드 생성/스케쥴링으로 인해 CPU가 바빠져 메모리 사용량 증가
  • 애플리케이션 성능 저하 가능성
  • 병렬 작업의 폭증으로 인한 스레드의 폭증을 막으려면 스레드 풀 사용 필요
    • 스레드 생성/수거에 대한 비용 방지

동작 원리

  • 스레드 풀: 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐에 들어오는 작업을 하나씩 스레드가 맡아 처리
  • 작업 처리가 끝난 스레드는 다시 작업 큐에서 새로운 작업을 가져와 처리
  • 작업 처리 요청이 폭증해도 작업큐에서 작업이 대기하다가 여유 있는 스레드가 그것을 처리하므로 스레드의 전체 개수는 일정함. 애플리케이션의 성능 저하도 일어나지 않음.

사용 방법
Java - ExecutorService 인터페이스 & Executors 클래스

장점
1. 프로그램 성능저하 방지: 매번 발생되는 작업을 병렬처리하기 위한 스레드 생성/수거에 따른 부담은 프로그램의 전체적인 퍼포먼스 저하시킴
2. 다수의 사용자 요청을 처리하기 위해: 다수의 사용자의 요청을 수용하고, 빠르게 처리하고 대응하기 위해

단점
1. 너무 많이 만들어두면 메모리 낭비: 쓰이지 않는 스레드는 아무일도 하지 않고 메모리만 차지
2. 유휴 스레드 발생: 여러 스레드가 병렬적으로 작업처리하면서 작업완료 소요시간이 다를 경우 A스레드는 계속해서 처리하는 동안 BC는 유휴 시간 발생
forkJoinPool로 방지

🌊 Monitor

  • 자바에서 모든 객체는 monitor를 가짐
  • Monitor는 여러 스레드가 객체로 동시에 객체로 접근하는 것을 막음
    • 상호 배제
  • 스레드가 monitor를 가지면 monitor를 가지는 객체에 lock을 걸 수 있음
    • 그럼 다른 스레드들은 해당 객체에 접근 불가

동작원리

  • condition 변수 존재: 같은 condition을 기다리는 프로세스의 큐
    • 시그널 연산 & 대기 연산 가능
    • 시그널 연산:
      • 작업큐가 비었으면 아무것도 안함
      • 대기 중인 작업을 깨우는 시그널 보냄
    • 대기 연산: 스레드를 항상 대기큐에 배치
    • First come first served
  • 객체에 synchronized 메서드가 호출되면 스레드는 이 메서드를 호출하는데, 이 때:
    • monitor가 누구한테도 '소유'되지 않았으면 스레드가 소유권을 가짐 > 메서드 호출 가능
    • monitor가 다른 스레드에 '소유'되었으면 호출 스레드는 monitor가 해제될 때까지 기다림
      • 스레드가 synchronized 메서드를 exit하면서 monitor를 해제함

장점
1. synchronization 코드가 모두 하나의 장소에 집중되어 있고, 코드를 사용하는 사람은 코드 구현을 몰라도 됨
2. 프로세스의 숫자에 영향 받지 않음. 원하는 프로세스의 수만큼에 사용 가능
3. 뮤텍스와 같이 해제할 필요 없음

Mutex vs. Monitor vs. Semaphore

  • Mutex: 스레드를 synchronize하기 위한 메커니짐
    • 자바의 모든 객체가 뮤텍스를 가짐
    • 한 번에 한 스레드만 객체에 접근할 수 있도록
    • 두가지 상태: unlocked & locked (boolean/binary number로 표현)
    • 상태는 직접적으로 제어 불가
      • 프로그래머가 객체의 뮤텍스를 해제할 수 없음
      • 자바 머신만 가능
    • synchronized 키워드가 붙은 코드 블락에서 뮤텍스 존재
  • Monitor: 뮤텍스 위의 상위구조
  • Semaphore: 카운터를 사용한 synchronization 메커니즘
    • 공유 자원에 동시 접근 가능한 최대 스레드 수
    • 생성자:
      • Semaphore(int permits)
      • Semaphore(int permits, boolean fair)
        • int permits: 카운터의 초기 & 최댓값. 공유 자원에 동시 접근 가능 최대 스레드 수
        • boolean fair: 어느 스레드가 접근 권한을 얻을지.
          • fair=true: 권한 요청 순서대로 권한 주어짐
          • faire=false: 스레드 스케쥴러가 순서 결정함

🌊 Fork-Join

분할 정복 알고리즘으로 재귀적으로 병렬처리

  • 작업 단위가 작으면 해당 작업 수행
  • 작업 단위가 작지 않으면 작업을 반으로 쪼개어 두 개의 작업으로 나누고, 두 작업을 동시에 실행시킴
  • work stealing 알고리즘 사용

Work Stealing: 여러개의 deque에서 작업 처리 진행시, 하나의 deque는 바쁘고 다른 하나는 여유로우면 여유있는 deque가 바쁜 deque의 꼬리에 있는 일을 가져가서 처리

  • 가장 큰 크기의 작업들에서 일을 가져오기 위해 글로벌 엔트리 큐 또는 다른 덱의 꼬리에서 일을 가져옴
  • 스레드가 작업을 위해 경쟁하는 것을 방지
  • 스레드가 일을 뺏어올 작업을 찾는 빈도수 최소화

사용 방식
1. 처리해야 할 작업을 모두 포함한 작업 생성
2. 작업을 처리할 ForkJoinPool 생성: ForkJoinPool pool = new ForkJoinPool();
3. 작업 수행

ForkJoinPool commonPool = ForkJoinPool.commonPool(); // ForkJoinPool에 public static 키워드 붙인 것과 같은 의미
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
  • commonPool() - 작업별로 스레드풀을 따로 만드는 것을 지양하기 때문에 자원 사용을 줄임

기타

  • ExecutorService에 구현된 메서드

참고:

profile
우당탕탕

0개의 댓글