열렬히.뛰기

암묵적 쓰레딩

운영체제 > Part 2 : 프로세스 관리 > 공룡책 4장 : 쓰레드 > 암묵적 쓰레딩

암묵적 쓰레딩이란?

쓰레딩을 프로그래머가 직접하지 않고, 커널이 하게 두는 것.

  • 쓰레드 대신 task에 대한 정의만 해주면 된다.

책에서는 5가지 방법을 소개했다.

  1. Thread pool
  2. Fork join (자바)
  3. OpenMP (C, C++)
  4. Grand central patch (애플)
  5. Thread Building Blocks (인텔)

Thread pool

다중 쓰레드 만들기의 문제점

  1. 쓰레드 생성 시 걸리는 시간
  2. 시스템에서 동시에 실행할 수 있는 쓰레드의 갯수

그래서 나온 아이디어 중 하나 = 쓰레드 풀

  • 일정한 수의 쓰레드를 미리 만들자는 전략
  • 쓰레드가 필요하면 이미 만들어진 쓰레드를 즉시 서비스
  • 서비스 완료 시 다시 쓰레드 풀로 돌아감.

장점

  1. 기존 쓰레드로 서비스하는 것이 더 빠르다.
  2. 존재할 수 있는 쓰레드 갯수에 제한을 둬서 시스템 과부하를 막는다.
  3. 태스크를 만드는 것과 태스크를 실행하는 것을 분리. (앞 단원 참고)
    • 태스크를 일정 시간 이후에 실행시킬 수 있음.

예시

windows에서는 다음과 같은 함수들을 제공한다.

c
DWORD WINAPI PoolFunction(PVOID param) {…} // 쓰레드에서 실행할 함수

QueueUserWorkItem(&PoolFunction, NULL, 0);
// 쓰레드 풀 속 쓰레드 중 하나가 PoolFunction을 
// 파라미터 'NULL'을 받으면서,  
// 생성 및 관리할 플래그로 '0'을 선정해 
// 실행한다. 

java.util.concurrent 패키지에선 다음 함수를 제공한다.

  • Executor.newSingleThreadExecutor() : 크기가 1인 풀 = 1개의 쓰레드 생성
  • Executor.newFixedThreadExecutor(int size) : 크기가 size인 풀 = size개의 쓰레드 생성
  • Executor.newCachedThreadPool() : 크기가 무제한인 풀 생성

Fork join (java)

  • 4.4장에서 다뤘던 동기식 방식과 같다.
  • fork → 자식 쓰레드 처리 → 자식 쓰레드 반환 → 부모 쓰레드 실행 → 종료

자바에서는 어떻게 암시적으로 동기적 방식을 다루는지 알아보자.

java
import java.util.concurrent.*;

class Sum implements extends RecursiveTask<Integer>
{
    static final int THRESHOLD = 1000;

    private int begin;
    private int end;
    private int[] array;

    public Sum(int begin, int end, int[] array) {
        this.begin = begin;
        this.end = end;
        this.array = array;
    }

    protected Integer compute() {
        if (end - begin < THRESHOLD) {
            // 쓰레드가 적을때
            int sum = 0;
            for (int i = begin; i < end; i++)
                sum += array[i];
            return sum;
        }

        else {
            // 쓰레드가 많을때
            int mid = (begin + end) / 2;

            Sum left = new Sum(begin, mid, array);
            Sum right = new Sum(begin, mid, array);
            left.fork();
            right.fork();

            return right.join() + left.join(); // join으로 결과 반환
        }
    }

}

public class thread {
    public static void main(String args[]) {
        int num = Integer.parseInt(args[0].trim);
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < num; i++)
            pool.execute(new Task()); // 쓰레드 'num'개 생성 및 실행
        pool.shutdown() // 모든 쓰레드들을 한꺼번에 종료
    }
}

OpenMP

c, cpp로 작성된 API와 컴파일러 디렉티브의 집합.

공유 메모리 환경에서 병렬 프로그래밍을 할 수 있게 도움을 줌.

  • 병렬로 실행될 수 있는 블록을 병렬 영역이라고 부름.
c

컴파일러와 물려서 쓰는 것

Grand central patch (by Apple)

병렬로 실행될 태스크를 식별할 수 있도록 하는 API, 런타임 라이브러리 등의 조합.

  • 태스크를 디스패치 큐에 넣어서 스케줄 함.
  • GCD는 직렬과 병행 2가지 유형의 디스패치 큐를 유지

thread-building block (by Intel)

C++에서 병렬 응용 프로그램 설계를 지원하는 템플릿 라이브러리.