CS/운영체제

스레드

D_Helloper 2023. 6. 13. 04:17

  • 클라이언트에게 수 많은 요청을 받는 웹 서버가 존재할 때, 스레드라는 개념이 없으면 발생하는 문제
    • 요청이 들어올 때 마다 새로운 프로세스를 생성해서 작업해야 함, 이는 자원, 시간 소요가 매우 크고 프로세스 간 컨텍스트 스위칭은 오버헤드가 큰 작업
    • IPC를 활용해야 하는데 이 또한 오버헤드가 큼
  • 멀티프로세싱의 무거움을 해소하기 위해 탄생한 개념 = 스레드

스레드 사용 이유

  • 프로세스에 비해 메모리를 적게 사용
  • 스레드 컨텍스트 스위칭이 프로세스 컨텍스트 스위칭보다 빠름
  • 스레드 간에는 메모리를 공유하므로 통신을 위해 IPC를 사용하지 않아도 됨

스레드란

  • 하나의 프로세스에서 실행 흐름의 단위
  • 하나의 프로세스 내에서 스레드 간에 공유하는 것
    • 코드 영역, 데이터 영역, 힙 영역
  • 하나의 프로세스 내에서 스레드 간에 공유하지 않는 것
    • 프로그램 카운터, 레지스터 정보, 스택 영역
    • 코드 실행과 직결된 부분

싱글 스레드

  • 하나의 프로세스에서 하나의 스레드만 실행

장점

  • 공유 자원을 접근하는 동기화 문제를 신경쓰지 않아도 됨
  • 컨텍스트 스위칭을 요구하지 않아서 전환 비용이 들지 않음

단점

  • 여러 CPU를 활용하지 못함

멀티 스레드

  • 하나의 프로세스에서 다수의 스레드 실행
  • 프로세스 내에서 자원을 공유하여 자원 생성과 관리 중복을 최소화
  • 서버가 많은 요청을 효율적으로 수행할 수 있는 환경 제공
  • 각각의 스레드가 고유의 레지스터와 스택으로 표현

장점

  • Responsiveness(응답성)
    • 동시에 여러 작업을 수행할 수 있으므로 응답 속도 향상
  • Resource Sharing(자원 공유)
    • 스레드 간 자원 공유를 통해 통신의 효율 증가
  • Economy(경제성)
    • 프로세스 생성에 비해 메모리와 자원 할당의 비용이 적음
  • Scalability(확장성)
    • 단일 스레드 프로세스는 CPU가 아무리 많아도 하나의 CPU만 사용 가능

단점

  • 메모리 공간을 공유하기 때문에 동기화, 교착 상태와 같은 문제가 발생 가능

스레드 컨텍스트 스위치

  • 프로세스 컨텍스트 스위치와 동작 방식은 동일
  • PCB(Process Control Block)과 마찬가지로 TCB(Thread Control Block)라는 자료 구조 존재
  • 스레드 컨텍스트 스위치가 빠른 이유
    • 다른 프로세스는 다른 메모리 주소를 가지기 때문에, 컨텍스트 스위칭을 하는 과정에서 다른 메모리 주소를 참조해야 하고, 캐시 버퍼를 필수적으로 사용
    • 스레드는 메모리 주소를 공유하기 때문에 계속 같은 메모리 주소만 참조하면 되므로 캐시, 버퍼를 사용하는 과정을 생략

멀티 스레딩

멀티 스레딩 사용 예

Java의 스레드 만들기 3가지

  1. Thread 클래스 상속
public class Threadtest extends Thread {
 
    public void run() {
        try {
            System.out.println("1번 방법 Thread test");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
  1. Runnable 인터페이스 구현
public class Threadtest2 implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println("2번 방법 Thread test");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

public class Threadtestmain {
 
    public static void main(String[] args) {

        Threadtest2 test=new Threadtest2();
        Thread test2 = new Thread(test);
        test2.start();
    }
}
  1. 람다 표현식 사용
public class Threadtestmain {
 
    public static void main(String[] args) {

        Thread test2 = new Thread(() -> {
						try {
		            System.out.println("3번 방법 Thread test");
		        } catch (Exception e) {
		            System.out.println(e.getMessage());
		        }
				});
        test2.start();
    }
}

자바에서의 스레드 관리

  • join
    • 자식 스레드가 join 메소드를 실행하면 부모는 자식이 끝날 때 까지 대기해야 함
  • interrupt
    • 스레드 종료

스레드의 종류

User Thread

  • 유저 모드에서 사용하는 스레드. 즉, 개발자가 만드는 스레드
  • 운영체제는 이 유저 모드의 스레드를 지원하지 않음, 정확히는 인식을 못함
  • 운영체제는 커널 모드에서 프로세스만 볼 수 있고, 프로세스가 유저 모드에서 각 스레드들을 보고 있는 구조, 따라서 운영체제가 PCB를 관리하고, 프로세스가 TCB를 관리
  • Green Thread 라고도 불림

장점

  • 스레드 컨텍스트 스위칭을 프로세스 내부에서 진행하기 때문에 오버헤드가 적음
  • 유저 모드와 커널 모드의 전환이 없으므로 오버헤드가 적음

단점

  • 프로세스 내에서 하나의 스레드가 중단되면 다른 스레드도 같이 중단
    • 커널이 유저 스레드를 인식하지 못하여 하나의 스레드가 중단되면 그냥 프로세스 자체를 대기상태로 바꿔두기 때문

Kernel Thread

  • 커널 모드에서 사용하는 스레드
  • 커널이 스레드의 생성 및 스케쥴링을 관리
  • native thread라고도 불림

장점

  • 커널이 알아서 스레드를 관리
  • 프로세스 내에서 하나의 스레드가 중단되어도 커널이 다른 스레드들을 계속 실행
  • 멀티 프로세싱 환경에서 스레드들을 각각 다른 프로세스에 할당 가능

단점

  • 스케쥴링과 동기화를 위해 시스템 콜을 호출하는 과정에서 오버헤드 발생
  • 유저 모드와 커널 모드의 전환이 빈번하므로 오버헤드가 많음

유저 스레드, 커널 스레드의 관계

  1. Many to One Model
    • 커널 스레드 1개에 유저 스레드 N개가 연결된 모델
    • 커널 스레드는 유저 스레드가 몇개 있는지 모르기 때문에 유저 스레드에서 I/O 작업이 하나라도 발생하면 해당 프로세스는 I/O가 풀릴 때 까지 block 상태가 됨
  2. One to One
    • 커널 스레드 1개에 유저 스레드 1개가 연결된 모델
    • 효율이 좋지 않음
  3. Many to Many
    • 커널 스레드 N개에 사용자 스레드 N개가 혼합되어 연결된 모델

Thread Pool

사용 이유

  • 프로세스 실행 중 병렬 작업 처리가 많아지면 스레드 개수가 증가, 그로 인한 스레드 생성 및 스케쥴링 작업 등의 이유로 CPU에 많은 부하가 갈 수 있음
  • 스레드를 매 작업마다 새로 생성하는 것이 아닌 미리 설정해 둔 개수로 만들어 두고, 작업이 요청될 때 마다 각 스레드에서 할당해서 처리하는 방식이 효율적
  • 이러한 과정을 Thread Pool 이라고 함