본문 바로가기
CS/OS(운영체제)

쓰레드(Thread)란

by 계범 2022. 1. 5.
운영체제 관련 글 순서

- 프로세스란
- 쓰레드
- CPU 스케줄링
- 동기화 툴
- 동시성 제어 예제
- 데드락
- 주 메모리
- 페이징과 스와핑
- 가상 메모리와 디맨드 페이징
- 페이지 교체 알고리즘(FIFO, OPT, LRU), 쓰레싱, working set

 

쓰레드(Thread)

 

  • lwp(lightweight process)라고도 한다.
  • 프로세스 내에서 실제로 작업을 수행하는 주체
  • 모든 프로세스는 한개 이상의 스레드가 존재하여 작업을 수행

 

 

쓰레드의 자원

 

  • 공통자원
    • code
    • data
    • heap
  • 독립적인 자원
    • registers
    • stack ( 지역변수, 매개변수, 리턴값)
    • pc(program counter)
    •  

 

 

쓰레드는 독립적인 작업을 수행하기 때문에 각각의 스택과 PC,레지스터 값을 가지고 있다.

 

각각의 스택과 레지스터를 가지고 있는 이유

해당 블로그 참조: https://goodgid.github.io/What-is-Thread/

 

 

멀티 쓰레드

 

: 하나의 프로세스에서 다수의 실행단위(Thread)을 실행할 수 있게 해주는 것.

 

 

장점

 

  • responsiveness
    • 논블럭으로 실행 가능
    • 요청이 들어오면 이후 일을 스레드에게 맡기고 요청을 계속 받고 있을 수 있음
  • resource sharing
    • 프로세스간의 통신시에는 shared memory 나 message queue를 써야하나, 쓰레드에선 공유 데이터가 있기에 리소스 공유에 좋다.
  • economy
    • 프로세스 생성하는것보다 비용적인 측면에서 좋다.
    • 컨텍스트 스위칭에서도 프로세스의 생성 및 컨텍스트 스위칭보다 빠름.
      • 멀티 쓰레드에서의 컨텍스트 스위칭은 데이터영역과 힙을 올리고 내릴 필요가 없음.
      • 캐시메모리를 비울 필요가 없다는 뜻.
  • scalability
    • 확장성에서 좋다.
    • 각각의 CPU 코어에 스레드를 붙여서 병렬처리 가능

 

단점

 

  • 동일한 자원에 동시에 접근할 경우 문제가 생김.
  • 이럴때 동기화 작업을 통해 작업 처리 순서 및 공유 리소스에 대한 접근을 컨트롤 해야함.
    • 뮤텍스, 세마포어
  • 불필요한 부분까지 동기화 할경우 병목현상 발생

멀티 스레드에서 컨테스트 스위칭이 일어날때

  1. 같은 프로세스 내 : TCB 사용(스레드의 메타데이터 저장 블록)
  2. 다른 프로세스의 스레드 : PCB& TCB 사용

 

 

 

멀티 프로세스 vs 멀티 스레드

 

 

: 멀티 프로세스는 독립적인 공간을 가지고 있어서, 하나가 문제가 발생하여도 다른 프로세스로 확산되지 않음.

 

멀티 스레드는 공통 자원을 사용하기때문에 하나가 장애를 일으키면 전체가 문제 생김.

 

하지만 멀티 스레드가 컨텍스트 스위칭에서 더 빠르고, 공유 자원을 활용하므로 효율적인 면에서 좋다.

 

 

 

 

자바에서는 스레드쓰는 방법

 

  • 쓰레드 클래스 상속받기
    • 새로운 클래스를 쓰레드 클래스를 상속받아서 실행
    • 대신 다중상속이 안되기때문에 안좋음
    • class MyThread1 extends Thread {
      	public void run() {
      		try {
      			while (true) {
      				System.out.println("Hello, Thread!");
      				Thread.sleep(500);
      			}
      		}
      		catch (InterruptedException ie) {
      			System.out.println("I'm interrupted");
              }
      	}
      }
    • public class ThreadExample1 {
      	public static final void main(String[] args) {
      		MyThread1 thread = new MyThread1();
      		thread.start();
      		System.out.println("Hello, My Child!");
          }
      }
  • runnable 인터페이스 상속하기
    • 새로운 클래스가 인터페이스를 상속받고 오버라이딩하여 실행
    • class MyThread2 implements Runnable {
      	public void run() {
      		try {
      			while (true) {
      				System.out.println("Hello, Runnable!");
      				Thread.sleep(500);
      			}
      		}
      		catch (InterruptedException ie) {
      			System.out.println("I'm interrupted");
      		}
      	}
      }
      
    • public class ThreadExample2 {
      	public static final void main(String[] args) {
      		Thread thread = new Thread(new MyThread2());
      		thread.start();
      		System.out.println("Hello, My Runnable Child!");
      	}
      }
      
  • lambda expression
    • 새로운 클래스를 생성하지않고, 익명클래스로 실행
    • public class ThreadExample3 {
      	public static final void main(String[] args) {
      		Runnable task = () -> {
      			try {
      				while (true) {
      					System.out.println("Hello, Lambda Runnable!");
      					Thread.sleep(500);
      				}
      			}
      			catch (InterruptedException ie) {
      				System.out.println("I'm interrupted");
      			}
      		};
      		Thread thread = new Thread(task);
      		thread.start();
      		System.out.println("Hello, My Lambda Child!");
          }
      }

 

 

자바 쓰레드 명령어

 

  • 쓰레드 시작: start()
  • 자식 쓰레드 끝날때까지 대기 : join()
  • 쓰레드 종료: interrupt()

 

 

멀티코어 시스템에서의 멀티 스레드

 

 

싱글 코어: time-sharing으로 실행

 

 

멀티코어: 병렬적으로 실행이 가능

 

멀티코어에서 고려해야할 것

  • identifying tasks: 작업 처리 방법 도출
  • balance: 작업량의 균형 맞추기
  • data splitting: 각 코어 별 데이터 나누기
  • data dependency: 동기적인 처리 동작을 위해 data 의존도를 고려
  • testing and debugging: 테스트 & 디버깅의 어려움

해당 블로그 참조: https://chanto11.tistory.com/63

 

 

병렬처리 방법

 

  1. data를 쪼개서 core에 분배
  2. 데이터는 두고 task를 쪼개서 분배


현재는 분산시스템이 가능(hadoop) 한개의 컴퓨터가 아닌 다량의 컴퓨터로 분산.

 

 

 

amdahl's law(암달의 법칙)

 

  • 코어는 무조건 많을수록 좋은가?
    • S: 병렬처리 가능한 부분 N: 코어의 개수
    • 병렬처리가 가능한 부분에 따라 코어를 늘림으로서 얻을 수 있는 성능향상이 다름.

 

 

쓰레드의 타입

 

  • 유저 쓰레드와 커널 쓰레드

 

java는 운영체제가 아니고 jvm임(virtual machine)

 

이 쓰레드는 운영체제가 가지고 있는 cpu를 넘나들 수 없음.(user thread)

 

 

User Thread

  • 커널 위의 유저공간에서 진행(커널은 스레드로 인식 x)
  • 스레드를 관리하는 라이브러리에서 생성 및 관리

 

  • 운영체제 시스템 내에서 생성되어 동작하는 스레드
  • 커널이 각 스레드를 개별적으로 관리 가능

 

커널 쓰레드와 유저 쓰레드의 관계

  • Many to one(user-level)

    • 커널 스레드 1 : 유저 스레드 n
    • 장점
      • 사용자 영역에서 생성되고 관리하여 속도가 빠름
      • 커널의 개입을 받지 않기때문에 이식성이 높음(모든 운영체제에서 가능)
    • 단점
      • 커널에선 스레드를 1개로 보기때문에 하나의 스레드가 중단되면 모든 스레드 중단
  • one to one(Kernel-Level)

    • 커널 스레드 1: 유저 스레드 1
    • 운영체제가 지원하는 스레드 기능으로 구현
    • 커널이 스레드의 생성 및 스케줄링 등을 관리
    • 장점
      • 커널이 각 스레드 개별 관리 -> 스레드 병행 처리 가능
      • 하나의 스레드가 중단되어도 다른 스레드는 실행 가능
    • 단점
      • 사용자 스레드에 비해 생성 및 관리 속도 느림
  • many to many(Combined)

    • 커널 스레드 n : 유저 스레드 n
    • 위의 두개의 스레드를 혼합하여 단점을 극복한 구조
    • 장점
      • 스레드 병행 처리 가능
      • 스레드 풀링 기법을 통해 일대일 스레드 매핑에서의 오버헤드를 줄임

해당 블로그 참조: https://yoongrammer.tistory.com/55

 

 

쓰레드 라이브러리(유저 스레드)

 

  • POSIX Pthreads(리눅스,유닉스에서 많이 씀)
  • Windows thread
  • Java thread(다양한 운영체제에서 가능)

 

POSIX Pthreads

  • int main(int argc, char *argv[])
    {
    	pthread_t tid; // thread identifier
    	pthread_attr_t attr; // thread attributes
    
    	pthread_attr_init(&attr);
    	pthread_create(&tid, &attr, runner, argv[1]);
    	pthread_join(tid, NULL);
    	printf("sum = %d\n", sum);
    }
    void *runner(void *param)
    {
    	int i, upper = atoi(param);
    	sum = 0;
    	for (i = 0; i <= upper; i++)
    		sum += i;
    	pthread_exit(0);
    }
  • runner() : 별도의 스레드는 해당 함수에서 실행
  • pthread_create(): 별도의 스레드 생성
  • pthread_join(): thread 종료될때까지 대기
  • pthread_exit(): 스레드 종료
  • 컴파일시 -pthread 걸고 해야함 ( gcc -pthread ***.c)

 

 

implicit Threading

 

 

:개발자가 스레드의 생성과 관리를 책임지지 않고 컴파일러와 라이브러리가 맡는 개념

 

 

  • Thread Pools
    • 풀을 만들어두고 거기서 쓰레드를 가져와서 사용
    • 쓰레드를 새로 만드는게 아니라 미리 만들어둔 쓰레드를 가져와서 사용하는것!
    • 다 쓴 후엔 쓰레드 풀에 반환
    • 새로운 스레드를 생성해서 쓰는것보다 더 빠르게 서비스 가능
  • Fork & Join
    • explicit threading 을 implicit threading으로 나누는법
    • 부모 스레드가 자식 스레드를 fork한 후 종료하길 기다렸다가 join하고 자식의 결과를 확인 한 후 결합하는 방법
    • fork단계에서 스레드가 직접 구축되지 않고 병렬 작업만 할당하게 됨.
    • pthread의 fork-join과 비슷하나, 개발자가 직접 스레드를 생성하지 않는다.
  • OpenMP
    • openMP는 지시어를 넣어주면 컴파일러가 멀티 스레딩을 지원
    • 공유 메모리 환경에서 병렬 실행 가능하게 해줌
    •  
    • #include <omp.h> #include <stdio.h> int main(){ // 병렬 영역 선언 #pragma omp parallel { printf("I am a parallel region"); } return 0; }
    • gcc - fopenmp ***.c
  • Grand Central Dispatch(GCD)
    • Mac,ios 전용 apple에서 개발한 기술

 

참조

[주니온님의 인프런 운영체제강의(공룡책)](https://www.inflearn.com/course/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EA%B3%B5%EB%A3%A1%EC%B1%85-%EC%A0%84%EA%B3%B5%EA%B0%95%EC%9D%98/dashboard)

[스레드와 동시성](https://chanto11.tistory.com/63)

[멀티스레드](https://goodgid.github.io/What-is-Multi-Thread/)

'CS > OS(운영체제)' 카테고리의 다른 글

데드락(Deadlock)  (0) 2022.01.05
동시성 제어 예제(Bounded-Buffer, Readers-Writers  (0) 2022.01.05
동기화 툴(프로세스 동기화)  (0) 2022.01.05
CPU 스케줄링  (0) 2022.01.05
프로세스(Process)란  (0) 2022.01.05

댓글