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

[Chapter 4. 스레드] 암묵적 스레딩과 사용 방법

by 베어 그릴스 2022. 8. 20.
320x100
본 정리는 운영체제(Operating System: Concepts) 9th edition과 22학년도 1학기 건국대학교 운영체제 수업을 바탕으로 하고 있습니다.

 

암묵적 스레딩 (Implicit Threading)


스레드의 생성과 관리에 대한 책임을 컴파일러와 실행시간 라이브러리에게 넘겨주는 것을 말한다.

( ↔︎ 명시적 스레딩 : 개발자가 API를 통해 직접 스레드를 관리 )

 

암묵적 스레딩은 개발자의 다중 스레드 응용 프로그램의 설계를 도와주는 한 가지 방법이며,

책에서는 이에 대한 3가지 접근법을 소개하고 있다.

 

 

1. 스레드 풀 (thread pool)


새로운 스레드를 매 요청마다 만들어주게 될 경우 아래와 같은 문제들이 발생한다.

1. 스레드를 생성하는 데에는 시간 소요가 발생

2. 무한정 새로운 스레드를 만들게 된다면 언젠가는 시스템 자원들이 고갈될 것

 

이에 대한 해결책으로 등장한 것이 스레드 풀이다.

즉, 프로세스를 시작할 때 일정한 수의 스레드들을 미리 풀(pool)로 만들어두는 것이다.

 

스레드 풀의 스레드들은 평소에는 하는 일 없이 일감을 기다리게 된다. 그러다가 한 개의 요청(스레드 생성)이 들어오면 이 풀에서 한 스레드에게 그것을 할당한다. 요청을 다 서비스 해 주었으면 그 스레드는 다시 풀로 돌아가 다음 작업을 기다린다.

 

* 스레드 풀에 남아있는 스레드가 없다면, 스레드 풀에 스레드가 생길 때까지 대기하여야 한다.

 

스레드 풀의 장점

  1. 새 스레드를 만들어 주기보다 기존 스레드로 서비스해 주는 것이 더 빠르다.
  2. 스레드 풀의 스레드 개수에 제한을 두어 많은 수의 스레드를 병렬 처리할 수 없는 시스템에 도움이 된다.
  3. 태스크의 생성과 태스크의 실행을 다르게 분리할 수 있다. 

 

*Windows API의 스레드 풀 관련 함수 제공

DWORD WINAPI PoolFunction(AVOID Param){
	/**
    * this function runs as a separate thread.
    **/
}

우선 위와 같은 별도의 스레드에서 실행될 함수가 정의된다.

 

QueueUSerWorkItem(&PoolFunction, NULL, 0);

첫번째 인자로 실행될 함수에 대한 포인터

두번째 인자로 Function에 전달될 매개변수

세번째 인자로 스레드 풀이 스레드를 생성하고 관리하는 방법을 가리키는 플래그를 보내준다.

 

이 호출은 프로그래머를 대신하여 스레드 풀의 한 스레드가 PoolFunction() 함수를 호출하게 한다.

 

 

2. OpenMP


 

C, C++, 또는 FORTRAN으로 작성된 API & 컴파일러 디렉티브(Compiler Directive, 컴파일러 지시문)의 집합

개발자에게 공유 메모리 환경에서 병렬 프로그래밍을 할 수 있도록 도움을 준다.

 

병렬 영역이란, 병렬로 실행될 수 있는 블록(block, 독립적으로 실행될 수 있는 작업의 단위)을 말한다.

 

개발자가 코드에 병렬 영역에 컴파일러 디렉티브를 삽입하면

이 디렉티브는 OpenMP 실행시간 라이브러리에 해당 영역을 병렬로 실행하라고 지시하게 된다.

 

아래는 printf() 문을 포함하고 있는 병렬 영역 위에 컴파일러 디렉티브가 사용되고 있는 예시이다.

#include <omp.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	/* sequential code */

	#pragma omp parallel
	{
		printf("I am a parallel region.");
	}

	/* sequential code */
        
	return 0;
}

 

OpenMP가 아래의  컴파일러 디렉티브를 만나게 되면,

#pragma omp parallel

시스템의 코어 개수만큼 스레드를 생성하며, 모든 스레드는 병렬 영역을 동시에 실행하게 된다.

 

필요한 스레드의 개수를 직접 설정하는 것도 가능하다.

 

 

3. Grand Central Dispatch (GCD)


Apple의 Mac OS X와 iOS 운영체제를 위한 기술이며,

이는 개발자가 병렬로 실행될 수 있는 영역을 식별할 수 있게 도와준다.

 

GCD는 블록을 식별하여 디스패치 큐에 넣는다. 아래는 블록의 예시이다. ({} 앞의 ^ 기호로 블록을 표시)

^{ printf("I am a block") }

 

디스패치 큐는 스레드 풀에서 가용 스레드를 선택, 이를 블록에게 할당하는 것으로 해당 블록이 실행될 수 있도록 스케줄링을 하는데,

GCD는 두가지 유형의 디스패치 큐를 갖는다.

 

1. 직렬(serial) 큐

블록은 FIFO 순서대로 제거된다.

블록은 큐에서 제거되면 다른 블록이 제거되기 전에 실행을 반드시 완료해야 한다.

 

2. 병렬(concurrent) 큐

직렬 큐와 동일하게 블록은 FIFO 순서대로 제거되나, 여러 블록이 동시에 제거될 수 있다.

따라서 블록들을 병렬로 실행하는 것이 가능하다.

 

 

728x90