대체 스레드 종류는 뭐가 이렇게 많나? 😡

대체 스레드 종류는 뭐가 이렇게 많나? 😡

많은 방황과 삽질 끝에 정리한 스레드의 종류


스레드(Thread)


이번에 알았지만 컴퓨터 과학에서 스레드라는 용어는 굉장히 범용적으로 사용되고 있었다.

실제로 스레드는 여러 종류가 존재하기 때문에 글의 문맥에 따라 어떤 스레드를 말하는지를 유추할 수 있어야 하는데, 나 같은(노베이스 비전공자) 사람이 이런 상황을 마주하면 굉장히 힘들어진다.

자바 고급 서적들(JVM 레벨의…)을 보다보면 커널 스레드, 유저 스레드, 그린 스레드, 혹은 그냥 접두사 다 떼 놓은 스레드 같은 용어들이 미친듯이 나오는데, 기본기가 없으니 이게 대체 무슨말들을 하는건지 알아들을수가 있어야지…


image


비유하자면, 나는 코딩을 처음 시작할 때 API가 뭔지 엄청 헷갈려했었는데, 이것과 굉장히 비슷한 느낌이었다. (API도 굉장히 범용적으로 사용되는 용어이므로…)

아무튼, 이러한 이유로 이번에 운영체제 과목을 공부하게 됐는데, 까먹으면 내가 나중에 다시 볼 거기도 하고, 나 같은 분들(노베이스 비전공자)께 조금이나마 도움이 되길 바라기도 하며 나름대로 정리한 내용들을 기록해본다.

우선 이 글은 스레드가 뭔지 궁금해 한번쯤 검색을 해 사전적인 정의라도 찾아봤다는 가정하에 기록한다.


사전 지식


  • 프로그램(Program)
    • 코드로 작성된 실행될 수 있는 애플리케이션. 하지만 메모리(RAM)를 할당받지 못하고 하드디스크에 저장돼있는 것
  • 프로세스(Process)
    • 프로그램이 운영체제로부터 메모리를 할당받아 실행된 것. 즉, 실행 된 프로그램
  • 스레드(Thread)
    • 프로세스의 실행 단위. 하나의 프로세스는 최소한 하나 이상의 스레드를 포함한다
  • 멀티 프로그래밍(Multi Programming) or 멀티 프로세스(Multi Process)
    • 메모리에 동시에 여러개의 프로세스가 올라가는 것. MPD(Multi-Programming Degree or Degree of Multi programming mpd) 라는 키워드가 있다
  • 멀티 태스킹(Multi Tasking)
    • CPU가 여러개의 프로세스를 동시에 처리하는 것. CPU가 프로세스1을 조금 작업하고 정지한 후 프로세스2를 작업하러 가는 것
  • 시분할(Time Sharing) or 시분할 시스템(TSS, Time Sharing System)
    • 멀티 태스킹을 위해 각 프로세스가 CPU를 점유할 수 있는 시간을 강제해둔 시스템
  • 멀티 프로세싱(Multi Processing)
    • 프로그램이 여러개의 코어를 활용하는 것. 병렬 프로그래밍이라고도 불린다
  • 멀티 스레딩(Multi Threading)
    • 하나의 프로세스가 여러개의 스레드를 갖는 것. 멀티스레딩을 활용하면 동시성을 보장할 수 있다
  • 동시성(Concurrency)
    • CPU가 동 시간대에는 단 하나의 작업을 처리하지만, 여러개의 작업을 조금씩 번갈아가며 처리하면 이 작업이 인간 입장에선 너무 빠르기 때문에 여러개의 작업이 동시에 처리되는 것 처럼 느낀다. 이를 동시성이라고 부른다
  • 병렬성(Parallel)
    • CPU가 동 시간대에 여러개의 작업을 처리하고 있는 것. 즉, 코어 1이 프로세스 1을 처리하고, 코어 2가 프로세스 2를 처리하면 동 시간대에 정확히 두개의 작업을 처리하고 있는 것이다. 이를 병렬성이라고 부른다


image

동시성과 병렬성의 차이



image

스레드 개념도


하드웨어 스레드(Hardware Thread)


우선 누구라도 듀얼 코어, 4코어 4스레드, 4코어 8스레드 어쩌고 하는 말들을 들어본 적이 있을 것이다.

여기서 말하는 스레드가 하드웨어 스레드이다.

누군가는 CPU 스레드라고 부르기도 하는 것 같다.


모든 코드(Instruction Set, 명령어)는 메모리에서 CPU로 옮겨진 후 실행(연산)되고, 이러한 결과는 다시 메모리에 반영된다.

하지만 CPU의 연산 속도에 비해 메모리에서 CPU로 코드를 옮겨오거나, CPU에서 메모리로 코드를 옮기는 속도는 상대적으로 매우매우매우 느리다.

즉, 하드웨어가 발전함에 따라 CPU가 일하는 속도가 너무 빨라졌기 때문에, 역설적으로 CPU가 할 일이 없어 손가락을 빨며 대기하는 상황이 생긴다는 것이다.


image


위 그림대로라면, CPU는 코드를 메모리로 옮기거나, 메모리에서 CPU로 가져오는 동안에는 연산 작업을 하지 못하게 된다.

즉, CPU의 성능을 온전하게 활용하지 못하고 클럭을 낭비하게 되는 것이다.


image

CPU의 상태


상황이 이러하니 나온 것이, CPU 내부에서 일을 하는 친구를 하나 더 만들자는 것.

즉, 인텔에서는 HT(하이퍼 스레딩)라 부르고, AMD에서는 SMT라고 부르는 기술이 탄생한다.

그림으로 보자면 하기와 같다.


image


여담이지만, CPU프로세서는 대부분 이음동의어로 사용되며, 코어라는 것은 CPU 내부에 존재하는 ALU와 같은 핵심 부품들을 통틀어 의미한다.


image


HT/SMT는 실제로 물리적인 코어는 하나만 있지만, 운영체제에게는 코어가 두개인 것처럼 속이는 기술이다.

따로 찾아보면 파이프라이닝이니 뭐니 해서 굉장히 내용이 깊고 심오하니, 한번쯤은 꼭 찾아보길 바란다 !


image


커널 스레드(Kernel Thread)


프로그래밍을 제외한 대부분의 매체에서 말하는 스레드는 이 커널 스레드라고 봐도 무방하다.

실제로, 프로그래밍 관련 서적에서도 커널 스레드가 가장 많이 나오기도 한다.

이 커널 스레드가 가장 헷갈렸는데, 커널 스레드를 부르는 다른 이름이 진절머리나게 많았기 때문이다.

이 커널 스레드를 어떤 이름으로 부르는지, 내가 본 용어들을 정리하자면 다음과 같고, 더 있을수도 있다.


  • 네이티브 스레드(Native Thread)
  • 운영체제 스레드(OS Thread)
  • 커널 레벨 스레드(Kernel Level Thread)
  • 운영체제 레벨 스레드(OS Level Thread)


이것들이 모두 같은걸 의미하는 용어라는걸 확신할 수 없는 상황(노베이스 비전공 -ㅅ-)에서 온갖 글들을 보고있으면 머리만 더 아파지는 것이다. (예를 들자면… 엥? 이건 또 뭐야? 같은 느낌?)

이제와서 나는 대부분의 상황에서 커널 스레드를 위의 용어들과 동일하다고 생각하며 보고 있으며, 실제로 그렇게 봐도 이해하는데 지장이 없었다.


image


운영체제도 하나의 프로그램으로서 하드디스크에 저장되어있고, 컴퓨터에 전원을 공급하면 운영체제가 실행되며 메모리(RAM)에 올라가 프로세스가 된다.

그리고 메모리에 항상 상주하는, 운영체제의 핵심적인 부분(코드)들이 바로 커널이라고 할 수 있다.

즉, 커널은 일반적으로 운영체제의 이음동의어다.


운영체제도 일단 프로그램이기 때문에, 내부적으로 최소한 한개 이상의 스레드를 가진다.

운영체제가 프로그램으로서 갖는 스레드를 바로 커널 스레드(혹은 위에 나열한 용어들...)라고 부르며, 운영체제 코드에 작성 된 CPU 스케쥴링같은 코드들에 직접적으로 영향을 받아 하드웨어 스레드를 점유하거나 여타 하드웨어들을 제어하는 것이 바로 이 커널 스레드이다.

즉, 4코어 8스레드 컴퓨터에서는 동시간대에 정확히 8개의 커널 스레드가 동작할 수 있는 것이다.

따라서 멀티코어 파워를 온전히 활용해야 하는 병렬 프로그래밍에 있어 매우 중요한 개념이라고 볼 수 있겠다.


유저 스레드(User Thread)


유저 스레드는 사용자 스레드, 혹은 유저 레벨 스레드(User Level Thread)라고 불리기도 하며, 이는 프로그래밍 언어에서 제공하는 스레드를 의미한다.

프로그래밍 언어로 프로그램을 개발하면, 내부적으로 최소한 한개 이상의 스레드를 갖게 되는데, 이렇게 추가적인 스레드를 만들고 제어하는 것들을 대부분의 프로그래밍 언어에서 지원하고 있다.

그리고 기본적으로 커널에서는 이 유저 스레드들의 존재를 모르며, 오직 프로세스를 대상으로 일련의 작업들을 진행한다. (커널은 프로세스인 줄 알았지만, 그게 실제로는 유저 스레드인 셈…)

그러니까 실제적으로 유저 스레드가 작업을 하려면 커널 스레드와 반드시 매핑이 되어야만 하며(하드웨어 스레드가 커널 스레드를 바라보므로), 커널 스레드와 매핑되지 않은 유저 스레드는 아무런 작업도 할 수 없는 상태가 된다.


여기서 약간 의문이 생겼고, 나름의 답을 내렸는데 이게 정답인지는 나도 잘 모르겠다.

위에서는 운영체제도 프로그램이라 하였다.

그리고, 현재 대부분의 운영체제는 C언어로 작성돼있다.

즉, 위에서 말한 커널 스레드도 C언어에서 제공하는 스레드 관련 코드로 작성됐을 것이라는 생각이 들었는데, 그렇다면 커널 스레드도 유저 스레드의 일종이라고 볼 수 있지 않을까? 라는 의문이 들었었다.

내 결론은 커널 스레드도 유저 스레드의 일종이며, 운영체제도 프로그램이긴 하지만, 아주 특별한 프로그램이기 때문에 마찬가지로 운영체제의 유저 스레드에 커널 스레드라는 특별한 이름을 붙여 부르게 된 것이 아닐까 싶다. (정확히 아시는분은 알려주세요 😭)


잡담이 길었는데, 내가 사용하는 자바에도 물론 유저 스레드를 지원하기 때문에 Thread라는 클래스와, 이에 관련 된 여러가지 클래스들이 존재한다.


public class Thread implements Runnable {
  // ...이하 생략

  public synchronized void start() {
    if (threadStatus != 0)
      throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
      start0();
      started = true;
    } finally {
      try {
        if (!started) {
          group.threadStartFailed(this);
        }
      } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
      }
    }
  }

  private native void start0();
  
  // ...이하 생략
}


모던 자바의 스레드는 특이한데, Thread.start()가 호출되면 start0() 이라는 JNI(Java Native Interface)를 통해 운영체제의 시스템 콜(System Call)을 호출하고, 이는 결과적으로 커널 스레드를 직접적으로 생성하여 유저 스레드(여기선 자바 스레드)와 매핑하게 된다.

즉, 모던 자바의 모든 유저 스레드는 커널을 직접 만들어 다이렉트로 연결되므로, 하나도 남김없이 커널의 CPU 스케쥴링에 직접적으로 영향을 받게 된다.

또한, 하나의 커널 스레드가 여러개의 유저 스레드를 관리하고, 하나의 유저 스레드가 여러개의 커널 스레드의 관리를 받는 Many-To-Many 모델을 채택해 멀티코어 파워를 온전히 활용할 수 있게 됐다.

정리하자면 모던 자바는 병렬 프로그래밍에 많은 최적화가 되어있는 셈이다. (모든 코어를 균등하게 잘 갈굴 수 있다…!)


image

모던 자바의 Thread 모델


그린 스레드(Green Thread)


스레드_인용

자바 최적화 中


극 초창기 자바인 버전 1.2이전의 자바 스레드를 의미한다.

극 초창기의 자바는 위와 다르게, 하나의 커널 스레드에 여러개의 유저 스레드가 매핑되는 Many-To-One 모델이었다고 한다.


image

자바 1.2 이전의 Thread 모델 -


즉, 동시성은 지원하지만, 병렬성은 지원 할 수 없는 모델이었기 때문에 멀티코어 파워를 온전히 활용 할 수 없었고, 이로 인한 성능 이슈로 인해 자바 커뮤니티에서 굉장히 많은 비판을 받았던 것 같다.

아무튼 모던 자바에서는 사용되지 않고 있는 것 같지만, 자바 고급 서적들에서는 의외로 자주 나오는 용어이기 때문에 추가하였다.


Reference




© 2022. All rights reserved.