티스토리 뷰

컴퓨터에서 일어나는 작업들

프로그램(program)컴퓨터에서 실행되는 명령어들의 모음이며 코드(code)와 데이터(data)로 구성되어 있다. 이러한 프로그램을 실행시키면 프로그램 안의 명령어들이 한 줄씩 실행되며 활동을하고 이를 프로세스(process)라고 한다.

 

 

프로그램은 보조 기억 장치에 저장되고 프로세스는 RAM 메모리에 할당된다. 하여 프로그램은 코드와 데이터를 프로세스 메모리로 로딩하여 불러들이기 위해 드라이브에서 메모리로 데이터를 불러들여 힙(heap)과 스택(stack) 메모리 공간에 할당한다.

 

 

 

프로그램은 여러 개가 동시에 실행할 수 있고 이를 멀티프로세싱(Multi-Processing)이라고 한다.

 

 


스레드 (Thread)

각 프로세스는 시스템의 종류, 프로세스의 특징에 따라 OS가 할당해주는 메모리 공간의 크기가 다르며, 각 프로세스는 독립된 메모리 공간을 사용한다. 기본적으로는 서로 다른 프로세스는 상대의 메모리 공간에 읽기/쓰기 기능을 진행할 수 없다.

 

일반적으로 대부분의 운영체제는 스레드(thread)기능을 제공한다. 스레드는 프로세스처럼 명령어를 한 줄씩 실행하는 기본 단위이며, 프로세스 내부에서 동작한다.

 

스레드의 특징
  • 스레드는 한 프로세스 안에 다수가 존재할 수 있다.
  • 스레드는 같은 프로세스 안의 다른 스레드와 메모리 공간을 공유할 수 있다.
  • 각 스레드는 자신만의 스택을 가진다.

 

 

 

싱글스레드(Single-thread)

프로그래밍을 처음 시작하면 대부분 main함수를 통해 하나의 기능을 제공하는 프로그램을 작성해 보았을 것이다. 이러한 '동시에 하나만 실행되는 프로그램'을 싱글스레드 프로그램이라 하고, 싱글스레드로만 작동하는 프로그램을 설계하고 구현하는 것을 싱글스레드 모델(single threaded model)이라고 한다.

 

 

멀티스레드(Multi-thread)

프로그램이 실행되면 기본적으로 존재하는 스레드를 메인 스레드(main thread)라고 한다. 위 싱글스레드는 메인 스레드로만 동작하는 것이라 볼 수 있다.

 

이와 반대로 하나의 프로세스 안에는 메인 스레드와 동시에 여러 스레드가 생성될 수 있다. 그리고 여러 스레드는 동시에 한꺼번에 실행이 된다. 이렇게 여러 스레드가 동시에 일을 처리하는 것을 멀티스레드 모델 혹은 멀티스레딩이라 한다.

 

단, 멀티스레드는 CPU 코어 수에 따라 하나의 작업을 병렬적으로 동시 처리를 할 수도 있고 만약 코어가 하나인 경우 하나의 작업을 하다가 여유가 생기는 경우 다른 작업으로 넘어가는 방식으로 처리한다.

 

 


스레드의 종료 시점

앞서 스레드가 무엇인지 이야기했다. 이제 C++코드로 스레드를 생성해보겠다.

 

스레드 예제
#include <iostream>
#include <thread>
using std::thread;

void func1() {
  for (int i = 0; i < 10; i++) {
    std::cout << "쓰레드 1 작동중! \n";
  }
}

void func2() {
  for (int i = 0; i < 10; i++) {
    std::cout << "쓰레드 2 작동중! \n";
  }
}

void func3() {
  for (int i = 0; i < 10; i++) {
    std::cout << "쓰레드 3 작동중! \n";
  }
}
int main() {
  thread t1(func1);
  thread t2(func2);
  thread t3(func3);
}
terminate called without an active exception
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 �동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중!

 

위 코드에서 main함수에 각 func 함수를 스레드로 선언하고 스레드 변수에 할당한다. 이를 통해 3가지의 스레드 함수가 동시에 동작하는 환경이 갖추어졌다.

 

하지만 결과를 보았을 때, 예상했던 30줄의 cout 문장이 아닌 "terminate called without an active exception"라는 경고문과 예상과 전혀 다른 출력 값들이 나왔다.

 

이 현상은 함수의 스코프에 대해 이해하면 쉽게 알 수 있는 현상인데, 우선 각 스레드는 메인스레드와 별개의 라이프사이클로 동작하고 있다고 앞서 말했다. 그런데 만약 각 스레드를 생성하고 할당해주는 메인스레드가 먼저 종료된다면 메인스레드에 할당되어 있는 다른 스레드들은 어떻게 되는가???

 

 

join 함수

앞선 예제에서 발생한 문제가 바로 이 문제이다. 이 문제를 해결하기 위해서는 스레드에서 지원하는 join함수를 사용하면 된다.

 

join함수 포함 스레드 동작
#include <iostream>
#include <thread>
using std::thread;

void func1() {
  for (int i = 0; i < 10; i++) {
    std::cout << "쓰레드 1 작동중! \n";
  }
}

void func2() {
  for (int i = 0; i < 10; i++) {
    std::cout << "쓰레드 2 작동중! \n";
  }
}

void func3() {
  for (int i = 0; i < 10; i++) {
    std::cout << "쓰레드 3 작동중! \n";
  }
}
int main() {
  thread t1(func1);
  thread t2(func2);
  thread t3(func3);

  t1.join();
  t2.join();
  t3.join();
}
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 �동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 1 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
�레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 2 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동��! 
쓰레드 3 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동중! 
쓰레드 3 작동중!

 

위 코드에서 join이라는 함수만 호출하였는데도 모든 스레드가 정상 동작하고 난 뒤에 메인스레드가 종료된다. 이는 join함수가 실행되면 해당 스레드가 모든 작업이 완료될때까지 자신이 속한 메인스레드의 스코프 종료를 대기시키기 때문이다.

 

즉, join함수를 사용하는 스레드가 먼저 종료된 경우 메인스레드는 바로 종료가되지만 그렇지 않다면 "스레드가 종료될때까지 기다려"라는 명령으로 메인스레드가 일시정지되어 있는 것이다.

 

 


스레드의 실행 순서

함수는 스코프가 존재한다. 각 스코프가 끝이나면 스택영역의 지역변수들은 자동으로 메모리에서 할당해제된다. 이러한 경우 어쩔 수 없이 스택영역에 선언되었던 스레드들도 할당해제 되기 때문에 동작을하다가 바로 취소되는 것이다.

 

또한, 2개 이상의 스레드가 존재하는 경우 어떤 스레드가 먼저 실행될지는 아무도 모른다. 이는 CPU가 각 스레드 함수가 동작 중 대기상태가 필요하면 다른 스레드 함수를 이어서 실행하는 방식으로 진행되기 때문이다. 물론 멀티 코어를 지원하는 CPU라면 여러 코어가 동시에 작업을 처리할 수는 있을 것이다.

 

스레드의 동작 순서

 

스레드를 생성하라는 함수가 실행되면 스레드가 시작된다. 시작된 스레드는 Runnable 상태가되어 명령어를 실행한다.

 

다른 무언가를 기다릴 때는 Blocked 상태로 변환된다. Blocked 상태는 말그대로 스레드가 일시 정지하는 상태이며 이후 기다리던 작업이 종료되면 다시 Runnable 상태로 전환되어 명령을 마저 실행한다.

 

모든 일을 마친 스레드는 Dead 상태가 되어 소멸되며 메인스레드에 종료 상태를 알린다.

 

 


좀비 프로세스

만약 메인 스레드가 먼저 종료되고 다른 스레드들이 아직 실행된다면 어떻게 될까? 이러한 상황에서 프로세스는 종료되지 않고 계속 남아있게 된다. 즉, 다른 프로그램이 종료되지 않는 현상이 나타나고 이를 좀비 프로세스라고 일컫는다. 

 

이는 멀티프로세싱에서 주의해야하는 현상 중 하나이며 스레드를 생성하는 함수는 운영체제마다 다르고 프로그래밍 언어마다 다르기 때문에 많은 경험이 요구되는 사항이기도 하다.

 

윈도우 스레드
DWORD t hreadID;
ThreadParam t hreadParam;
t hreadParam.value = 123;
CreateThread(..., ThreadProc, &threadParam, ..., &threadID) ;
리눅스 스레드
pthread_t t hread;
ThreadParam t hreadParam;
t hreadParam.value = 123;
t hread_creat e(&t hread, NULL, ThreadProc, &threadParam );
모던 C++ 스레드
st d: :thread t 1(ThreadPree, 123 );

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함